├── testsuite
├── src
│ ├── it
│ │ ├── resources
│ │ │ ├── CtrlC.exe
│ │ │ ├── README.md
│ │ │ └── JSONtoEnum.java
│ │ └── java
│ │ │ └── io
│ │ │ └── quarkus
│ │ │ └── ts
│ │ │ └── startstop
│ │ │ ├── ibm
│ │ │ ├── ArtifactGeneratorIbmTest.java
│ │ │ └── CodeQuarkusIbmSiteTest.java
│ │ │ ├── redhat
│ │ │ ├── ArtifactGeneratorRedHatTest.java
│ │ │ └── CodeQuarkusRedHatSiteTest.java
│ │ │ ├── utils
│ │ │ ├── TestFlags.java
│ │ │ ├── UnitTestResource.java
│ │ │ ├── RunCommandAugmentor.java
│ │ │ ├── WhitelistProductBomJars.java
│ │ │ ├── Apps.java
│ │ │ ├── FakeOIDCServer.java
│ │ │ ├── MvnCmds.java
│ │ │ ├── URLContent.java
│ │ │ ├── WebpageTester.java
│ │ │ ├── OpenTelemetryCollector.java
│ │ │ ├── AsyncProfiler.java
│ │ │ └── LogBuilder.java
│ │ │ ├── NativeDebugTest.java
│ │ │ ├── QuarkusMavenPluginTest.java
│ │ │ ├── ArtifactGeneratorBOMTest.java
│ │ │ ├── SpecialCharsTest.java
│ │ │ ├── StartStopTest.java
│ │ │ └── CodeQuarkusTest.java
│ ├── test
│ │ └── java
│ │ │ └── io
│ │ │ └── quarkus
│ │ │ └── ts
│ │ │ └── startstop
│ │ │ └── DummyTest.java
│ └── main
│ │ └── resources
│ │ └── log4j2.xml
└── pom.xml
├── app-jakarta-rest-minimal
├── src
│ └── main
│ │ ├── resources
│ │ ├── application.properties
│ │ └── META-INF
│ │ │ └── resources
│ │ │ └── index.html
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── quarkus
│ │ ├── QuarkusRestApplication.java
│ │ └── HelloController.java
├── threshold.properties
└── pom.xml
├── app-full-microprofile
├── src
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── quarkus
│ │ │ ├── serialization
│ │ │ ├── dto
│ │ │ │ ├── MyEnum.java
│ │ │ │ ├── ComplexDto.java
│ │ │ │ ├── NestedRecord.java
│ │ │ │ ├── NestedClass.java
│ │ │ │ └── NestedInterface.java
│ │ │ └── JsonResource.java
│ │ │ ├── QuarkusRestApplication.java
│ │ │ ├── HelloController.java
│ │ │ ├── client
│ │ │ ├── ServiceController.java
│ │ │ ├── Service.java
│ │ │ └── ClientController.java
│ │ │ ├── health
│ │ │ ├── ServiceLiveHealthCheck.java
│ │ │ └── ServiceReadyHealthCheck.java
│ │ │ ├── resilient
│ │ │ └── ResilienceController.java
│ │ │ ├── config
│ │ │ └── ConfigTestController.java
│ │ │ └── metric
│ │ │ └── MetricController.java
│ │ └── resources
│ │ ├── application.properties
│ │ └── META-INF
│ │ └── resources
│ │ └── index.html
├── threshold.properties
└── pom.xml
├── app-generated-skeleton
├── index.html
├── AddedController.java
├── threshold.properties
├── settings.xml
├── README.md
└── application.properties
├── .github
├── dependabot.yml
├── mvn-settings.xml
└── workflows
│ └── ci.yaml
├── .gitignore
├── ACKNOWLEDGEMENTS.md
├── .mvn
└── wrapper
│ └── maven-wrapper.properties
├── mvnw.cmd
├── pom.xml
├── mvnw
└── LICENSE
/testsuite/src/it/resources/CtrlC.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/quarkus-qe/quarkus-startstop/HEAD/testsuite/src/it/resources/CtrlC.exe
--------------------------------------------------------------------------------
/app-jakarta-rest-minimal/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | injected.value=Injected value
2 | value=lookup value
3 |
4 | quarkus.ssl.native=true
5 |
6 | quarkus.console.color=false
7 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/serialization/dto/MyEnum.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.serialization.dto;
2 |
3 | public enum MyEnum {
4 | ONE,
5 | TWO,
6 | THREE
7 | }
8 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/serialization/dto/ComplexDto.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.serialization.dto;
2 |
3 | public record ComplexDto(MyEnum myEnum, NestedClass nestedClass, NestedInterface nestedInterface) {
4 | }
5 |
--------------------------------------------------------------------------------
/testsuite/src/test/java/io/quarkus/ts/startstop/DummyTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | public class DummyTest {
6 |
7 | @Test
8 | public void dummyTest() {
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/app-generated-skeleton/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Landing Page
6 |
7 |
8 |
9 | Congratulation! You are on landing page.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/QuarkusRestApplication.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus;
2 |
3 | import jakarta.ws.rs.ApplicationPath;
4 | import jakarta.ws.rs.core.Application;
5 |
6 | @ApplicationPath("/data")
7 | public class QuarkusRestApplication extends Application {
8 | }
9 |
--------------------------------------------------------------------------------
/app-jakarta-rest-minimal/src/main/java/com/example/quarkus/QuarkusRestApplication.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus;
2 |
3 | import jakarta.ws.rs.ApplicationPath;
4 | import jakarta.ws.rs.core.Application;
5 |
6 | @ApplicationPath("/data")
7 | public class QuarkusRestApplication extends Application {
8 | }
9 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/serialization/dto/NestedRecord.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.serialization.dto;
2 |
3 | public record NestedRecord(String recordProperty) {
4 |
5 | public NestedRecord() {
6 | this("record-property-value");
7 | }
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/app-generated-skeleton/AddedController.java:
--------------------------------------------------------------------------------
1 | package org.my.group;
2 |
3 | import jakarta.inject.Singleton;
4 | import jakarta.ws.rs.GET;
5 | import jakarta.ws.rs.Path;
6 |
7 | @Path("/hello-added")
8 | @Singleton
9 | public class AddedController {
10 | @GET
11 | public String sayHello() {
12 | return "Hello added";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app-generated-skeleton/threshold.properties:
--------------------------------------------------------------------------------
1 | # With all extensions enabled, it could take the dev mode a long time to start.
2 | linux.generated.dev.time.to.first.ok.request.threshold.ms=54000
3 | linux.generated.dev.time.to.reload.threshold.ms=30000
4 | windows.generated.dev.time.to.first.ok.request.threshold.ms=60000
5 | windows.generated.dev.time.to.reload.threshold.ms=30000
6 |
--------------------------------------------------------------------------------
/app-jakarta-rest-minimal/src/main/resources/META-INF/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello from a simple Jakarta REST app.
6 |
7 |
8 | Simple Jakarta REST
9 | Hello Jakarta REST endpoint
10 |
11 |
--------------------------------------------------------------------------------
/app-generated-skeleton/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app-jakarta-rest-minimal/src/main/java/com/example/quarkus/HelloController.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus;
2 |
3 | import jakarta.inject.Singleton;
4 | import jakarta.ws.rs.GET;
5 | import jakarta.ws.rs.Path;
6 |
7 | @Path("/hello")
8 | @Singleton
9 | public class HelloController {
10 | @GET
11 | public String sayHello() {
12 | return "Hello World";
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/HelloController.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus;
2 |
3 | import jakarta.inject.Singleton;
4 | import jakarta.ws.rs.GET;
5 | import jakarta.ws.rs.Path;
6 |
7 | @Path("/hello")
8 | @Singleton
9 | public class HelloController {
10 |
11 | @GET
12 | public String sayHello() {
13 | return "Hello World";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app-full-microprofile/threshold.properties:
--------------------------------------------------------------------------------
1 | linux.jvm.time.to.first.ok.request.threshold.ms=2500
2 | linux.jvm.RSS.threshold.kB=220000
3 | linux.native.time.to.first.ok.request.threshold.ms=70
4 | linux.native.RSS.threshold.kB=90000
5 | windows.jvm.time.to.first.ok.request.threshold.ms=3500
6 | windows.jvm.RSS.threshold.kB=5000
7 | windows.native.time.to.first.ok.request.threshold.ms=500
8 | windows.native.RSS.threshold.kB=5000
9 |
--------------------------------------------------------------------------------
/app-jakarta-rest-minimal/threshold.properties:
--------------------------------------------------------------------------------
1 | linux.jvm.time.to.first.ok.request.threshold.ms=2100
2 | linux.jvm.RSS.threshold.kB=150000
3 | linux.native.time.to.first.ok.request.threshold.ms=50
4 | linux.native.RSS.threshold.kB=60000
5 | windows.jvm.time.to.first.ok.request.threshold.ms=4500
6 | windows.jvm.RSS.threshold.kB=4800
7 | windows.native.time.to.first.ok.request.threshold.ms=500
8 | windows.native.RSS.threshold.kB=4800
9 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/ibm/ArtifactGeneratorIbmTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.ibm;
2 |
3 | import io.quarkus.ts.startstop.ArtifactGeneratorTest;
4 | import org.junit.jupiter.api.Tag;
5 |
6 | @Tag("product")
7 | public class ArtifactGeneratorIbmTest extends ArtifactGeneratorTest {
8 |
9 | @Override
10 | protected String getOfferingString() {
11 | return "ibm";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/redhat/ArtifactGeneratorRedHatTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.redhat;
2 |
3 | import io.quarkus.ts.startstop.ArtifactGeneratorTest;
4 | import org.junit.jupiter.api.Tag;
5 |
6 | @Tag("product")
7 | public class ArtifactGeneratorRedHatTest extends ArtifactGeneratorTest {
8 |
9 | @Override
10 | protected String getOfferingString() {
11 | return "redhat";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/serialization/dto/NestedClass.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.serialization.dto;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnore;
4 |
5 | public class NestedClass {
6 |
7 | public String getProperty() {
8 | return "property-value";
9 | }
10 |
11 | @JsonIgnore
12 | public String getIgnoredProperty() {
13 | return "ignored-value";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/client/ServiceController.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.client;
2 |
3 | import jakarta.ws.rs.GET;
4 | import jakarta.ws.rs.Path;
5 | import jakarta.ws.rs.PathParam;
6 |
7 | @Path("/client/service")
8 | public class ServiceController {
9 |
10 | @GET
11 | @Path("/{parameter}")
12 | public String doSomething(@PathParam("parameter") String parameter) {
13 | return String.format("Processed parameter value '%s'", parameter);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "maven" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/client/Service.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.client;
2 |
3 | import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
4 |
5 | import jakarta.enterprise.context.ApplicationScoped;
6 | import jakarta.ws.rs.GET;
7 | import jakarta.ws.rs.Path;
8 | import jakarta.ws.rs.PathParam;
9 |
10 | @RegisterRestClient
11 | @ApplicationScoped
12 | public interface Service {
13 |
14 | @GET
15 | @Path("/{parameter}")
16 | String doSomething(@PathParam("parameter") String parameter);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | injected.value=Injected value
2 | value=lookup value
3 |
4 | com.example.quarkus.client.Service/mp-rest/url=http://localhost:8080/data/client/service
5 |
6 | quarkus.ssl.native=true
7 | quarkus.console.color=false
8 |
9 | # address of simplistic Vert.x collector started before 'app-full-microprofile' project is tested
10 | quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4317/api/traces
11 | # shorten waiting period for traces
12 | quarkus.otel.bsp.schedule.delay=700ms
13 | quarkus.otel.bsp.export.timeout=700ms
14 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/TestFlags.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | /**
4 | * Some flags to drive the tests flow
5 | */
6 | public enum TestFlags {
7 | WARM_UP("This run is just a warm up for Dev mode."),
8 | RESTEASY_REACTIVE("Using RESTEasy Reactive extensions"),
9 | QUARKUS_BOM("platformArtifactId will use quarkus-bom"),
10 | PRODUCT_BOM("platformArtifactId will use quarkus-bom from Product");
11 | public final String label;
12 |
13 | TestFlags(String label) {
14 | this.label = label;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | # IDEA
26 | .idea/*
27 | *.iml
28 | *.ipr
29 | *.iws
30 | target
31 |
32 | # VS Code
33 | .classpath
34 | .factorypath
35 | .project
36 | .settings
37 | .vscode
38 |
39 | # Maven
40 | impsort-maven-cache.properties
--------------------------------------------------------------------------------
/testsuite/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/UnitTestResource.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import java.io.Closeable;
4 | import java.util.function.Supplier;
5 |
6 | public interface UnitTestResource extends Closeable {
7 |
8 | Supplier NOOP_SUPPLIER = () -> new UnitTestResource() {
9 |
10 | @Override
11 | public void close() {
12 |
13 | }
14 |
15 | @Override
16 | public void reset() {
17 |
18 | }
19 |
20 | };
21 |
22 | /**
23 | * Resets resources so that next test can start with a clean sheet.
24 | */
25 | void reset();
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/health/ServiceLiveHealthCheck.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.health;
2 |
3 | import org.eclipse.microprofile.health.HealthCheck;
4 | import org.eclipse.microprofile.health.HealthCheckResponse;
5 | import org.eclipse.microprofile.health.Liveness;
6 |
7 | import jakarta.enterprise.context.ApplicationScoped;
8 |
9 | @Liveness
10 | @ApplicationScoped
11 | public class ServiceLiveHealthCheck implements HealthCheck {
12 |
13 | @Override
14 | public HealthCheckResponse call() {
15 |
16 | return HealthCheckResponse.named(ServiceLiveHealthCheck.class.getSimpleName()).withData("live", true).up().build();
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/health/ServiceReadyHealthCheck.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.health;
2 |
3 | import org.eclipse.microprofile.health.HealthCheck;
4 | import org.eclipse.microprofile.health.HealthCheckResponse;
5 | import org.eclipse.microprofile.health.Readiness;
6 |
7 | import jakarta.enterprise.context.ApplicationScoped;
8 |
9 | @Readiness
10 | @ApplicationScoped
11 | public class ServiceReadyHealthCheck implements HealthCheck {
12 |
13 | @Override
14 | public HealthCheckResponse call() {
15 |
16 | return HealthCheckResponse.named(ServiceReadyHealthCheck.class.getSimpleName()).withData("ready", true).up().build();
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ACKNOWLEDGEMENTS.md:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | ## MicroProfile Starter
3 | Test applications ```app-full-microprofile``` and ```app-jakarta-rest-minimal``` are modified versions of code
4 | originally generated by [https://start.microprofile.io/](https://start.microprofile.io/).
5 | Some utility methods were adopted from [https://github.com/eclipse/microprofile-starter/](https://github.com/eclipse/microprofile-starter/)
6 | and modified to suit the limited purpose of this TS. The adopted work was authored
7 | by Michal Karm Babacek and licensed under Apache 2 license.
8 |
9 | ## Windows console and CTRL+C
10 | The method of handling the CTRL+C was adopted from hints by [Stanislav](http://stackoverflow.com/a/15281070/32453) and [rogerdpack](https://stackoverflow.com/users/32453/rogerdpack).
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/client/ClientController.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.client;
2 |
3 | import org.eclipse.microprofile.rest.client.inject.RestClient;
4 |
5 | import jakarta.enterprise.context.ApplicationScoped;
6 | import jakarta.inject.Inject;
7 | import jakarta.ws.rs.GET;
8 | import jakarta.ws.rs.Path;
9 | import jakarta.ws.rs.PathParam;
10 |
11 | @Path("/client")
12 | @ApplicationScoped
13 | public class ClientController {
14 |
15 | @Inject
16 | @RestClient
17 | // https://quarkus.io/guides/cdi-reference#private-members
18 | // - private Service service;
19 | Service service;
20 |
21 | @GET
22 | @Path("/test/{parameter}")
23 | public String onClientSide(@PathParam("parameter") String parameter) {
24 | return service.doSomething(parameter);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/serialization/dto/NestedInterface.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.serialization.dto;
2 |
3 | public interface NestedInterface {
4 |
5 | NestedInterface INSTANCE = new NestedInterface() {
6 | @Override
7 | public String getString() {
8 | return "Vive la résistance!";
9 | }
10 |
11 | @Override
12 | public int getInt() {
13 | return 42;
14 | }
15 |
16 | @Override
17 | public NestedRecord getRecord() {
18 | return new NestedRecord();
19 | }
20 |
21 | @Override
22 | public char getCharacter() {
23 | return Character.MAX_VALUE;
24 | }
25 | };
26 |
27 | String getString();
28 |
29 | int getInt();
30 |
31 | Object getRecord();
32 |
33 | char getCharacter();
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app-generated-skeleton/README.md:
--------------------------------------------------------------------------------
1 | This is a placeholder directory to keep
2 | [threshold.properties](./threshold.properties) related to generated artifacts.
3 |
4 | It also keeps dummy [application.properties](./application.properties) used for the
5 | generated skeleton purely to satisfy some extensions' expected non-empty directives.
6 |
7 | Another resource that kept here is an [AddedController](./AddedController.java) class,
8 | which is used to test live reload of added classes.
9 |
10 | Apart from that, no functional code is actually run for the skeleton though.
11 | It merely checks the generator and Hello World in dev mode.
12 |
13 | When executed, the test actually uses ```ARTIFACT_GENERATOR_WORKSPACE``` directory
14 | to generate the ```app-generated-skeleton``` project into. If neither sys prop nor env prop ```ARTIFACT_GENERATOR_WORKSPACE```
15 | is set, it defaults to ```java.io.tmpdir``` on your system.
16 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/resilient/ResilienceController.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.resilient;
2 |
3 | import org.eclipse.microprofile.faulttolerance.Fallback;
4 | import org.eclipse.microprofile.faulttolerance.Timeout;
5 |
6 | import jakarta.enterprise.context.ApplicationScoped;
7 | import jakarta.ws.rs.GET;
8 | import jakarta.ws.rs.Path;
9 |
10 | @Path("/resilience")
11 | @ApplicationScoped
12 | public class ResilienceController {
13 |
14 | @Fallback(fallbackMethod = "fallback")
15 | @Timeout(500)
16 | @GET
17 | public String checkTimeout() {
18 | try {
19 | Thread.sleep(700L);
20 | } catch (InterruptedException e) {
21 | // Silence is golden
22 | }
23 | return "Never from normal processing";
24 | }
25 |
26 | public String fallback() {
27 | return "Fallback answer due to timeout";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/serialization/JsonResource.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.serialization;
2 |
3 | import com.example.quarkus.serialization.dto.ComplexDto;
4 | import com.example.quarkus.serialization.dto.MyEnum;
5 | import com.example.quarkus.serialization.dto.NestedClass;
6 | import com.example.quarkus.serialization.dto.NestedInterface;
7 |
8 | import jakarta.ws.rs.GET;
9 | import jakarta.ws.rs.Path;
10 | import jakarta.ws.rs.Produces;
11 | import jakarta.ws.rs.core.MediaType;
12 |
13 | /**
14 | * Tests DTO serialization as this is most likely done by majority of apps these days.
15 | */
16 | @Produces(MediaType.APPLICATION_JSON)
17 | @Path("serialization/json")
18 | public class JsonResource {
19 |
20 | @GET
21 | @Path("complex-dto")
22 | public ComplexDto complexDto() {
23 | return new ComplexDto(MyEnum.ONE, new NestedClass(), NestedInterface.INSTANCE);
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/app-generated-skeleton/application.properties:
--------------------------------------------------------------------------------
1 | # This is a FAKE configuration to boot the generated empty project.
2 | # No code actually uses it at runtime to show any examples/quickstarts.
3 |
4 | quarkus.oidc.auth-server-url=http://localhost:6661/auth/realms/quarkus
5 | quarkus.oidc.client-id=666
6 | quarkus.oidc-client.auth-server-url=http://localhost:6667/auth/realms/quarkus
7 | quarkus.oidc-client.client-id=667
8 | quarkus.oidc-client.token-path=token
9 | quarkus.oidc-client.discovery-enabled=false
10 | quarkus.otel.traces.exporter=none
11 | quarkus.datasource.jdbc.url=jdbc:postgresql://localhost/quarkus_test
12 | quarkus.datasource.jdbc.driver=org.postgresql.Driver
13 | quarkus.datasource.username=quarkus_test
14 | quarkus.datasource.password=quarkus_test
15 | quarkus.hibernate-orm.schema-management.strategy=drop-and-create
16 | quarkus.console.color=false
17 | quarkus.devservices.enabled=false
18 | # TODO remove when https://github.com/quarkusio/quarkus/issues/50793 is fixed
19 | quarkus.hibernate-orm.sql-load-script=
20 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
20 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/config/ConfigTestController.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.config;
2 |
3 | import org.eclipse.microprofile.config.Config;
4 | import org.eclipse.microprofile.config.ConfigProvider;
5 | import org.eclipse.microprofile.config.inject.ConfigProperty;
6 |
7 | import jakarta.enterprise.context.RequestScoped;
8 | import jakarta.inject.Inject;
9 | import jakarta.ws.rs.GET;
10 | import jakarta.ws.rs.Path;
11 |
12 | @Path("/config")
13 | @RequestScoped
14 | public class ConfigTestController {
15 |
16 | @Inject
17 | @ConfigProperty(name = "injected.value")
18 | // https://quarkus.io/guides/cdi-reference#private-members
19 | // - private String injectedValue;
20 | String injectedValue;
21 |
22 | @Path("/injected")
23 | @GET
24 | public String getInjectedConfigValue() {
25 | return "Config value as Injected by CDI " + injectedValue;
26 | }
27 |
28 | @Path("/lookup")
29 | @GET
30 | public String getLookupConfigValue() {
31 | Config config = ConfigProvider.getConfig();
32 | String value = config.getValue("value", String.class);
33 | return "Config value from ConfigProvider " + value;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/resources/META-INF/resources/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello from a full MicroProfile suite
6 |
7 |
8 |
9 | Hello from a full MicroProfile suite
10 |
11 | Hello Jakarta REST endpoint
12 |
13 | Config
14 | Injected config values
15 | Config values by lookup
16 |
17 | Fault tolerance
18 | Fallback after timeout
19 |
20 | Health
21 | Health (Live) status (with custom status)
22 | Health (Ready) status (with custom status)
23 |
24 | Metrics
25 | Timed endpoint
26 | Metrics page
27 |
28 | JWT Auth
29 | Call Secured endpoint with JWT in Authorization Header
30 |
31 | Open API
32 | Open API Documentation
33 |
34 | Rest Client
35 | Call REST endpoint using generated client based on interface
36 |
37 |
--------------------------------------------------------------------------------
/testsuite/src/it/resources/README.md:
--------------------------------------------------------------------------------
1 | # CtrlC.exe
2 |
3 | When you start Quarkus on Windows as a process without
4 | a console window, you cannot stop it gently with TASKKILL,
5 | you have to use TASKKILL /F. That terminates the process immediately
6 | and it does not let it write "stopped in" to the process output.
7 |
8 | To mitigate this, you have to use a native Windows API with
9 | a small tool that detaches itself from its own console,
10 | attaches to the non-existing console of the PID you want
11 | to stop and sends it Ctrl+C.
12 |
13 | ```$c
14 |
15 | #include
16 | #include
17 |
18 | void ctrlC(int pid) {
19 | FreeConsole();
20 | if (AttachConsole(pid)) {
21 | SetConsoleCtrlHandler(NULL, true);
22 | GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
23 | }
24 | exit(1);
25 | }
26 |
27 | int main(int argc, const char* argv[]) {
28 | if (argc != 2) {
29 | printf("provide a pid number");
30 | return 1;
31 | }
32 | int pid = atoi(argv[1]);
33 | printf("stopping pid %d", pid);
34 | ctrlC(pid);
35 | return 0;
36 | }
37 |
38 | ```
39 |
40 | # JSONtoEnum.java
41 |
42 | This is a utility that generates a convenience enum from a Code Quarkus website instance, e.g:
43 |
44 | ```
45 | javac JSONtoEnum.java && java JSONtoEnum https://code.quarkus.stage.redhat.com/api/extensions?platformOnly=false
46 | ```
47 |
48 | # API of code.quarkus
49 | https://editor.swagger.io/?url=https://code.quarkus.io/q/openapi
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/RunCommandAugmentor.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | public class RunCommandAugmentor {
8 |
9 | public static List setCommandPrefix(List baseCommand, List commandPrefix) {
10 | if (commandPrefix == null || (commandPrefix.size() == 1 && commandPrefix.get(0).length() == 0)) {
11 | return baseCommand;
12 | }
13 | List runCmd = new ArrayList<>(baseCommand.size() + commandPrefix.size());
14 | runCmd.addAll(commandPrefix);
15 | for (String cmdPart : baseCommand) {
16 | runCmd.add(cmdPart);
17 | }
18 | return Collections.unmodifiableList(runCmd);
19 | }
20 |
21 | public static List setMemoryLimits(List baseCommand, List memory, boolean isNative) {
22 | if (memory == null || (memory.size() == 1 && memory.get(0).length() == 0)) {
23 | return baseCommand;
24 | }
25 | List runCmd = new ArrayList<>(baseCommand.size() + memory.size());
26 | for (String cmdPart : baseCommand) {
27 | runCmd.add(cmdPart);
28 | if (cmdPart.equals(Commands.JAVA_BIN)) {
29 | runCmd.addAll(memory);
30 | }
31 | }
32 | // for native command add memory at the end of the command
33 | if (isNative) {
34 | runCmd.addAll(memory);
35 | }
36 | return Collections.unmodifiableList(runCmd);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app-full-microprofile/src/main/java/com/example/quarkus/metric/MetricController.java:
--------------------------------------------------------------------------------
1 | package com.example.quarkus.metric;
2 |
3 | import java.util.Random;
4 |
5 | import io.micrometer.core.annotation.Timed;
6 | import io.micrometer.core.instrument.Counter;
7 | import io.micrometer.core.instrument.MeterRegistry;
8 | import jakarta.enterprise.context.ApplicationScoped;
9 | import jakarta.ws.rs.GET;
10 | import jakarta.ws.rs.Path;
11 |
12 | @Path("/metric")
13 | @ApplicationScoped //Required for @Gauge
14 | public class MetricController {
15 |
16 | private final MeterRegistry registry;
17 |
18 | public MetricController(MeterRegistry registry) {
19 | this.registry = registry;
20 | registry.gauge("counter_gauge", this, MetricController::getCustomerCount);
21 | }
22 |
23 | @Path("timed")
24 | @Timed(value = "timed-request")
25 | @GET
26 | public String timedRequest() {
27 | // Demo, not production style
28 | int wait = new Random().nextInt(1000);
29 | try {
30 | Thread.sleep(wait);
31 | } catch (InterruptedException e) {
32 | // Demo
33 | e.printStackTrace();
34 | }
35 |
36 | return "Request is used in statistics, check with the Metrics call.";
37 | }
38 |
39 | @Path("increment")
40 | @GET
41 | public long doIncrement() {
42 | getEndpointCounter().increment();
43 | return (long) getEndpointCounter().count();
44 | }
45 |
46 | long getCustomerCount() {
47 | return (long) getEndpointCounter().count();
48 | }
49 |
50 | private Counter getEndpointCounter() {
51 | return registry.counter("endpoint_counter");
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/WhitelistProductBomJars.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | /**
4 | * Whitelists jar names.
5 | *
6 | * There are basically known issues. The enum should be empty.
7 | */
8 | public enum WhitelistProductBomJars {
9 | PRODUCT_BOM(new String[]{
10 | "org.checkerframework.checker-qual",
11 | "com.microsoft.sqlserver.mssql-jdbc",
12 | "com.microsoft.azure.adal4j",
13 | "com.nimbusds.oauth2-oidc-sdk",
14 | "com.nimbusds.content-type",
15 | "io.github.crac",
16 | // jna and jna-platform is dependency of io.quarkus:quarkus-jdbc-mariadb
17 | "net.java.dev.jna.jna",
18 | "net.java.dev.jna.jna-platform",
19 | "com.github.victools.jsonschema-generator", // https://issues.redhat.com/browse/QUARKUS-6713
20 | "org.apache.opennlp.opennlp-tools", // https://issues.redhat.com/browse/QUARKUS-6715
21 | "com.knuddels.jtokkit", // https://issues.redhat.com/browse/QUARKUS-6716
22 | // https://issues.redhat.com/browse/QUARKUS-6714
23 | "org.apache.poi.poi",
24 | "io.quarkiverse.poi.quarkus-poi",
25 | "io.quarkus.quarkus-awt",
26 | "org.apache.commons.commons-collections4",
27 | "org.apache.commons.commons-math3",
28 | "com.zaxxer.SparseBitSet",
29 | "org.apache.poi.poi-ooxml",
30 | "org.apache.xmlbeans.xmlbeans",
31 | "com.github.virtuald.curvesapi",
32 | "org.apache.poi.poi-ooxml-full",
33 | "org.apache.poi.poi-scratchpad"
34 | });
35 |
36 | public final String[] jarNames;
37 |
38 | WhitelistProductBomJars(String[] jarNames) {
39 | this.jarNames = jarNames;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/.github/mvn-settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | google-mirror-jboss-proxy
7 |
8 |
9 | google-maven-central
10 | GCS Maven Central mirror EU
11 | https://maven-central-eu.storage-download.googleapis.com/repos/central/data/
12 |
13 |
14 | jboss-maven-central-proxy
15 | JBoss Maven Central proxy
16 | https://repository.jboss.org/nexus/content/repositories/central/
17 |
18 |
19 | gradle-official-repository
20 | Gradle Official Repository
21 | https://repo.gradle.org/gradle/libs-releases-local/
22 |
23 |
24 |
25 |
26 | google-maven-central
27 | GCS Maven Central mirror EU
28 | https://maven-central-eu.storage-download.googleapis.com/repos/central/data/
29 |
30 |
31 | jboss-maven-central-proxy
32 | JBoss Maven Central proxy
33 | https://repository.jboss.org/nexus/content/repositories/central/
34 |
35 |
36 |
37 |
38 |
39 | google-mirror-jboss-proxy
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app-jakarta-rest-minimal/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkus.ts.startstop
6 | parent
7 | 1.0.0-SNAPSHOT
8 | ..
9 |
10 | app-jakarta-rest-minimal
11 |
12 |
13 |
14 | ${quarkus.platform.group-id}
15 | quarkus-bom
16 | ${quarkus.version}
17 | pom
18 | import
19 |
20 |
21 |
22 |
23 |
24 | io.quarkus
25 | quarkus-rest
26 |
27 |
28 |
29 | quarkus
30 |
31 |
32 | ${quarkus.platform.group-id}
33 | quarkus-maven-plugin
34 | ${quarkus.version}
35 |
36 |
37 |
38 | build
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | native
48 |
49 |
50 | native
51 |
52 |
53 |
54 | true
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/Apps.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 |
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 | import java.util.Properties;
12 |
13 | import static io.quarkus.ts.startstop.StartStopTest.BASE_DIR;
14 | import static org.junit.jupiter.api.Assertions.fail;
15 |
16 | public enum Apps {
17 | JAKARTA_REST_MINIMAL("app-jakarta-rest-minimal", URLContent.JAKARTA_REST_MINIMAL, WhitelistLogLines.JAKARTA_REST_MINIMAL),
18 | FULL_MICROPROFILE("app-full-microprofile", URLContent.FULL_MICROPROFILE, WhitelistLogLines.FULL_MICROPROFILE),
19 | GENERATED_SKELETON("app-generated-skeleton", URLContent.GENERATED_SKELETON, WhitelistLogLines.GENERATED_SKELETON);
20 |
21 | public final String dir;
22 | public final URLContent urlContent;
23 | public final WhitelistLogLines whitelistLogLines;
24 | public final Map thresholdProperties = new HashMap<>();
25 |
26 | Apps(String dir, URLContent urlContent, WhitelistLogLines whitelistLogLines) {
27 | this.dir = dir;
28 | this.urlContent = urlContent;
29 | this.whitelistLogLines = whitelistLogLines;
30 | File tpFile = new File(BASE_DIR + File.separator + dir + File.separator + "threshold.properties");
31 | String appDirNormalized = dir.toUpperCase().replace('-', '_') + "_";
32 | try (InputStream input = new FileInputStream(tpFile)) {
33 | Properties props = new Properties();
34 | props.load(input);
35 | for (String pn : props.stringPropertyNames()) {
36 | String normPn = pn.toUpperCase().replace('.', '_');
37 | String env = System.getenv().get(appDirNormalized + normPn);
38 | if (StringUtils.isNotBlank(env)) {
39 | props.replace(pn, env);
40 | }
41 | String sys = System.getProperty(appDirNormalized + normPn);
42 | if (StringUtils.isNotBlank(sys)) {
43 | props.replace(pn, sys);
44 | }
45 | thresholdProperties.put(pn, Long.parseLong(props.getProperty(pn)));
46 | }
47 | } catch (NumberFormatException e) {
48 | fail("Check threshold.properties and Sys and Env variables (upper case, underscores instead of dots). " +
49 | "All values are expected to be of type long.");
50 | } catch (IOException e) {
51 | fail("Couldn't find " + tpFile.getAbsolutePath());
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/FakeOIDCServer.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import org.jboss.logging.Logger;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.DataOutputStream;
7 | import java.io.IOException;
8 | import java.io.InputStreamReader;
9 | import java.net.InetAddress;
10 | import java.net.ServerSocket;
11 | import java.net.Socket;
12 | import java.nio.charset.StandardCharsets;
13 | import java.util.concurrent.atomic.AtomicBoolean;
14 |
15 | public class FakeOIDCServer implements Runnable {
16 | private static final Logger LOGGER = Logger.getLogger(FakeOIDCServer.class.getName());
17 | private final ServerSocket server;
18 | private final AtomicBoolean running = new AtomicBoolean(true);
19 |
20 | public FakeOIDCServer(int bindPort, String bindAddress) throws Exception {
21 | server = new ServerSocket(bindPort, 10, InetAddress.getByName(bindAddress));
22 | Thread thread = new Thread(this);
23 | thread.start();
24 | }
25 |
26 | @Override
27 | public void run() {
28 | try {
29 | while (running.get()) {
30 | Socket s = server.accept();
31 | s.setSoTimeout(500);
32 | BufferedReader i = new BufferedReader(new InputStreamReader(s.getInputStream(), StandardCharsets.UTF_8));
33 | DataOutputStream o = new DataOutputStream(s.getOutputStream());
34 | String l;
35 | StringBuilder sb = new StringBuilder(256);
36 | while ((l = i.readLine()) != null && l.length() > 0) {
37 | sb.append(l);
38 | sb.append('\n');
39 | }
40 | LOGGER.info("Quarkus OICD extension said: " + sb.toString());
41 | o.writeBytes("HTTP/1.1 200 OK\r\n" +
42 | "Connection: keep-alive\r\n" +
43 | "Content-Type: application/json\r\n" +
44 | "Content-Length: 52\r\n" +
45 | "\r\n" +
46 | "{\"id_token\":\"fakefakefakefakefake\", \"state\":\"12345\"}");
47 | o.flush();
48 | }
49 | } catch (IOException e) {
50 | // Silence is golden. We don't care about socket closed etc.
51 | } finally {
52 | stop();
53 | }
54 | }
55 |
56 | public void stop() {
57 | running.set(false);
58 | synchronized (this) {
59 | if (server != null) {
60 | try {
61 | server.close();
62 | } catch (IOException e) {
63 | // Silence is golden.
64 | }
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/MvnCmds.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import java.util.stream.Stream;
4 |
5 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusGroupId;
6 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusNativeProperties;
7 | import static io.quarkus.ts.startstop.utils.Commands.getLocalMavenRepoDir;
8 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusVersion;
9 |
10 | /**
11 | * Maven commands
12 | */
13 | public enum MvnCmds {
14 | JVM(new String[][]{
15 | new String[]{"mvn", "clean", "dependency:tree", "compile", "quarkus:build", "-Dquarkus.package.output-name=quarkus"},
16 | new String[]{Commands.JAVA_BIN, "-jar", "target/quarkus-app/quarkus-run.jar"}
17 | }),
18 | DEV(new String[][]{
19 | new String[]{"mvn", "clean", "quarkus:dependency-tree", "quarkus:dev", "-Dmaven.repo.local=" + getLocalMavenRepoDir(), "-Dquarkus.analytics.disabled=true"}
20 | }),
21 | NATIVE(new String[][]{
22 | Stream.concat(Stream.of("mvn", "clean", "compile", "package", "-Pnative"),
23 | getQuarkusNativeProperties().stream()).toArray(String[]::new),
24 | new String[]{Commands.isThisWindows ? "target\\quarkus-runner.exe" : "./target/quarkus-runner"}
25 | }),
26 | GENERATOR(new String[][]{
27 | new String[]{
28 | "mvn",
29 | getQuarkusGroupId() + ":quarkus-maven-plugin:" + getQuarkusVersion() + ":create",
30 | "-DprojectGroupId=my-groupId",
31 | "-DprojectArtifactId=" + Apps.GENERATED_SKELETON.dir,
32 | "-DprojectVersion=1.0.0-SNAPSHOT",
33 | "-DpackageName=org.my.group"
34 | }
35 | }),
36 | MVNW_DEV(new String[][]{
37 | new String[]{Commands.mvnw(), "-e", "quarkus:dev", "-Dquarkus.analytics.disabled=true"}
38 | }),
39 | MVNW_JVM(new String[][]{
40 | new String[]{Commands.mvnw(), "clean", "dependency:tree", "compile", "quarkus:build", "-Dquarkus.package.output-name=quarkus"},
41 | new String[]{Commands.JAVA_BIN, "-jar", "target/quarkus-app/quarkus-run.jar"}
42 | }),
43 | MVNW_NATIVE(new String[][]{
44 | Stream.concat(Stream.of(Commands.mvnw(), "clean", "dependency:tree", "compile", "package", "-Pnative", "-Dquarkus.package.output-name=quarkus"),
45 | getQuarkusNativeProperties().stream()).toArray(String[]::new),
46 | new String[]{Commands.isThisWindows ? "target\\quarkus-runner" : "./target/quarkus-runner"}
47 | });
48 |
49 | public final String[][] mvnCmds;
50 |
51 | MvnCmds(String[][] mvnCmds) {
52 | this.mvnCmds = mvnCmds;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/testsuite/src/it/resources/JSONtoEnum.java:
--------------------------------------------------------------------------------
1 | import java.net.URI;
2 | import java.nio.charset.StandardCharsets;
3 | import java.util.Scanner;
4 | import java.util.Set;
5 | import java.util.TreeSet;
6 | import java.util.regex.Matcher;
7 | import java.util.regex.Pattern;
8 |
9 | public class JSONtoEnum {
10 | public static Pattern pattern = Pattern.compile("(?i).*\"id\":\"[^:]*:([^\"]*)\".*\"shortId\":\"([^\"]*)\".*\"name\":\"([^\"]*)\".*\"tags\":\\[([^]]*)].*");
11 |
12 | public static void main(String[] args) throws Exception {
13 | if (args.length < 1 || !args[0].startsWith("http")) {
14 | System.out.println("Expects URL e.g. https://code.quarkus.stage.redhat.com/api/extensions");
15 | System.exit(1);
16 | }
17 | StringBuilder s = new StringBuilder();
18 | s.append("public enum CodeQuarkusExtensions {\n\n");
19 | Set usedIds = new TreeSet<>();
20 | URI uri = URI.create(args[0]);
21 | new Scanner(uri.toURL().openStream(), StandardCharsets.UTF_8).useDelimiter("},\\{").tokens().forEach(x -> {
22 | Matcher m = pattern.matcher(x);
23 | if (m.matches() && m.groupCount() == 4) {
24 | String id = m.group(1);
25 | if (!usedIds.contains(id)) {
26 | String shortId = m.group(2);
27 | String name = m.group(3);
28 | boolean supported = m.group(4).contains("supported");
29 | String label = id.replace("-", "_").toUpperCase();
30 | s.append(" ");
31 | s.append(label);
32 | s.append("(\"");
33 | s.append(id);
34 | s.append("\", \"");
35 | s.append(name);
36 | s.append("\", \"");
37 | s.append(shortId);
38 | s.append("\", ");
39 | s.append(supported);
40 | s.append("),\n");
41 | usedIds.add(id);
42 | }
43 | }
44 | });
45 | s.replace(s.length() - 2, s.length() - 1, ";");
46 | s.append("\n public final String id;" +
47 | "\n public final String name;" +
48 | "\n public final String shortId;" +
49 | "\n public final boolean supported;" +
50 | "\n\n" +
51 | " CodeQuarkusExtensions(String id, String name, String shortId, boolean supported) {\n" +
52 | " this.id = id;\n" +
53 | " this.name = name;\n" +
54 | " this.shortId = shortId;\n" +
55 | " this.supported = supported;\n" +
56 | " }\n" +
57 | "}\n");
58 | System.out.println(s.toString());
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/URLContent.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import static io.quarkus.ts.startstop.utils.OpenTelemetryCollector.GET_HELLO_INVOCATION_TRACED;
4 | import static io.quarkus.ts.startstop.utils.OpenTelemetryCollector.GET_HELLO_TRACES_URL;
5 |
6 | /**
7 | * Available endpoitns and expected content.
8 | */
9 | public enum URLContent {
10 | JAKARTA_REST_MINIMAL(new String[][]{
11 | new String[]{"http://localhost:8080/data/hello", "Hello World"},
12 | new String[]{"http://localhost:8080", "Hello from a simple Jakarta REST app."}
13 | }),
14 | FULL_MICROPROFILE(new String[][]{
15 | new String[]{"http://localhost:8080/q/health/ready", "\"UP\""},
16 | new String[]{"http://localhost:8080", "Hello from a full MicroProfile suite"},
17 | new String[]{"http://localhost:8080/data/hello", "Hello World"},
18 | new String[]{"http://localhost:8080/data/config/injected", "Config value as Injected by CDI Injected value"},
19 | new String[]{"http://localhost:8080/data/config/lookup", "Config value from ConfigProvider lookup value"},
20 | new String[]{"http://localhost:8080/data/resilience", "Fallback answer due to timeout"},
21 | new String[]{"http://localhost:8080/data/metric/timed", "Request is used in statistics, check with the Metrics call."},
22 | new String[]{"http://localhost:8080/q/metrics", "timed_request_seconds_count{class=\"com.example.quarkus.metric.MetricController\",exception=\"none\",method=\"timedRequest\"}"},
23 | new String[]{"http://localhost:8080/data/metric/increment", "1"}, // check counter incremented exactly once
24 | new String[]{"http://localhost:8080/q/metrics", "endpoint_counter_total 1.0"}, // counter metric must match
25 | new String[]{"http://localhost:8080/q/metrics", "counter_gauge"}, // check gauge for the counter exists
26 | new String[]{"http://localhost:8080/data/serialization/json/complex-dto", "Vive la résistance!"},
27 | new String[]{"http://localhost:8080/q/openapi", "/resilience"},
28 | new String[]{"http://localhost:8080/data/client/test/parameterValue=xxx", "Processed parameter value 'parameterValue=xxx'"},
29 | new String[]{GET_HELLO_TRACES_URL, GET_HELLO_INVOCATION_TRACED}
30 | }),
31 | GENERATED_SKELETON(new String[][]{
32 | new String[]{"http://localhost:8080/", "Congratulation! You are on landing page."},
33 | new String[]{"http://localhost:8080/hello", "Bye RESTEasy"},
34 | new String[]{"http://localhost:8080/hello-added", "Hello added"},
35 | new String[]{"http://localhost:8080/hello", "Bye from Quarkus REST"},
36 | });
37 |
38 | public final String[][] urlContent;
39 |
40 | URLContent(String[][] urlContent) {
41 | this.urlContent = urlContent;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app-full-microprofile/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkus.ts.startstop
6 | parent
7 | 1.0.0-SNAPSHOT
8 | ..
9 |
10 | app-full-microprofile
11 |
12 |
13 |
14 | ${quarkus.platform.group-id}
15 | quarkus-bom
16 | ${quarkus.version}
17 | pom
18 | import
19 |
20 |
21 |
22 |
23 |
24 | io.quarkus
25 | quarkus-rest-jackson
26 |
27 |
28 | io.quarkus
29 | quarkus-smallrye-fault-tolerance
30 |
31 |
32 | io.quarkus
33 | quarkus-micrometer-registry-prometheus
34 |
35 |
36 | io.quarkus
37 | quarkus-smallrye-health
38 |
39 |
40 | io.quarkus
41 | quarkus-smallrye-openapi
42 |
43 |
44 | io.quarkus
45 | quarkus-opentelemetry
46 |
47 |
48 | io.quarkus
49 | quarkus-rest-client
50 |
51 |
52 |
53 | quarkus
54 |
55 |
56 | ${quarkus.platform.group-id}
57 | quarkus-maven-plugin
58 | ${quarkus.version}
59 |
60 |
61 |
62 | build
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | native
72 |
73 |
74 | native
75 |
76 |
77 |
78 | true
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/WebpageTester.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.jboss.logging.Logger;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.net.URI;
9 | import java.net.URLConnection;
10 | import java.nio.charset.StandardCharsets;
11 | import java.util.Scanner;
12 | import java.util.concurrent.locks.LockSupport;
13 |
14 | import static org.junit.jupiter.api.Assertions.assertTrue;
15 |
16 | public class WebpageTester {
17 | private static final Logger LOGGER = Logger.getLogger(WebpageTester.class.getName());
18 |
19 | /**
20 | * Patiently try to wait for a web page and examine it
21 | *
22 | * @param url address
23 | * @param timeoutS in seconds
24 | * @param stringToLookFor string must be present on the page
25 | */
26 | public static long testWeb(String url, long timeoutS, String stringToLookFor, boolean measureTime) throws InterruptedException, IOException {
27 | if (StringUtils.isBlank(url)) {
28 | throw new IllegalArgumentException("url must not be empty");
29 | }
30 | if (timeoutS < 0) {
31 | throw new IllegalArgumentException("timeoutS must be positive");
32 | }
33 | if (StringUtils.isBlank(stringToLookFor)) {
34 | throw new IllegalArgumentException("stringToLookFor must contain a non-empty string");
35 | }
36 | String webPage = "";
37 |
38 | long now = System.currentTimeMillis();
39 | final long startTime = now;
40 | boolean found = false;
41 | long foundTimestamp = -1L;
42 | while (now - startTime < 1000 * timeoutS) {
43 | URLConnection c = URI.create(url).toURL().openConnection();
44 | c.setRequestProperty("Accept", "*/*");
45 | c.setConnectTimeout(500);
46 | try (InputStream in = c.getInputStream();
47 | Scanner scanner = new Scanner(in, StandardCharsets.UTF_8.toString())) {
48 | scanner.useDelimiter("\\A");
49 | webPage = scanner.hasNext() ? scanner.next() : "";
50 | } catch (Exception e) {
51 | LOGGER.debug("Waiting `" + stringToLookFor + "' to appear on " + url);
52 | }
53 | if (webPage.contains(stringToLookFor)) {
54 | found = true;
55 | if (measureTime) {
56 | foundTimestamp = System.currentTimeMillis();
57 | }
58 | break;
59 | }
60 | if (!measureTime) {
61 | Thread.sleep(500);
62 | } else {
63 | LockSupport.parkNanos(100000);
64 | }
65 | now = System.currentTimeMillis();
66 | }
67 |
68 | String failureMessage = "Timeout " + timeoutS + "s was reached. " +
69 | (StringUtils.isNotBlank(webPage) ? webPage + " must contain string: " : "Empty webpage does not contain string: ") +
70 | "`" + stringToLookFor + "'";
71 | if (!found) {
72 | LOGGER.info(failureMessage);
73 | }
74 | assertTrue(found, failureMessage);
75 | return foundTimestamp - startTime;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/testsuite/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkus.ts.startstop
6 | parent
7 | 1.0.0-SNAPSHOT
8 | ..
9 |
10 | testsuite
11 |
12 |
13 | commons-io
14 | commons-io
15 | ${commons.io.version}
16 | test
17 |
18 |
19 | org.apache.commons
20 | commons-lang3
21 | ${commons.lang.version}
22 | test
23 |
24 |
25 | org.junit.jupiter
26 | junit-jupiter
27 | ${junit.jupiter.version}
28 | test
29 |
30 |
31 |
32 | io.vertx
33 | vertx-grpcio-server
34 | ${vertx-grpcio-server.version}
35 | test
36 |
37 |
38 | org.yaml
39 | snakeyaml
40 | ${snakeyaml.version}
41 | test
42 |
43 |
44 |
45 |
46 |
47 | org.apache.maven.plugins
48 | maven-compiler-plugin
49 | ${maven.compiler.version}
50 |
51 |
52 | org.apache.maven.plugins
53 | maven-surefire-plugin
54 | ${maven.surefire.version}
55 |
56 |
57 | ${includeTags}
58 |
59 | ${excludeTags}
60 |
61 | ${exclude.tests}
62 |
63 |
64 |
65 |
66 | org.apache.maven.plugins
67 | maven-failsafe-plugin
68 | ${maven.failsafe.version}
69 |
70 |
71 |
72 | integration-test
73 | verify
74 |
75 |
76 |
77 |
78 |
79 | org.codehaus.mojo
80 | build-helper-maven-plugin
81 | ${maven.build.helper.version}
82 |
83 |
84 | add-integration-test-source-as-test-sources
85 | generate-test-sources
86 |
87 | add-test-source
88 |
89 |
90 |
91 | src/it/java
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/NativeDebugTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop;
2 |
3 | import io.quarkus.ts.startstop.utils.Apps;
4 | import io.quarkus.ts.startstop.utils.MvnCmds;
5 | import io.quarkus.ts.startstop.utils.Commands;
6 | import org.jboss.logging.Logger;
7 | import org.junit.jupiter.api.Tag;
8 | import org.junit.jupiter.api.Test;
9 | import org.junit.jupiter.api.TestInfo;
10 | import org.junit.jupiter.api.condition.EnabledOnOs;
11 | import org.junit.jupiter.api.condition.OS;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.nio.file.Files;
16 | import java.nio.file.Paths;
17 | import java.util.ArrayList;
18 | import java.util.Arrays;
19 | import java.util.List;
20 | import java.util.concurrent.ExecutorService;
21 | import java.util.concurrent.Executors;
22 | import java.util.concurrent.TimeUnit;
23 |
24 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusGroupId;
25 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusVersion;
26 | import static io.quarkus.ts.startstop.utils.Commands.getBuildCommand;
27 | import static io.quarkus.ts.startstop.utils.Commands.cleanTarget;
28 | import static io.quarkus.ts.startstop.utils.Commands.getBaseDir;
29 | import static io.quarkus.ts.startstop.utils.Logs.archiveLog;
30 | import static io.quarkus.ts.startstop.utils.Logs.checkLog;
31 | import static org.junit.jupiter.api.Assertions.assertTrue;
32 |
33 | /**
34 | * Tests Quarkus native mode with "-Dquarkus.native.debug.enabled=true"
35 | * Verify that debug symbols are created as well as the source cache
36 | */
37 | @Tag("native")
38 | @EnabledOnOs({OS.LINUX}) // debug details are available only on Linux
39 | public class NativeDebugTest {
40 |
41 | private final Apps app = Apps.JAKARTA_REST_MINIMAL;
42 | private final MvnCmds mvnCmds = MvnCmds.NATIVE;
43 |
44 | private static final Logger LOGGER = Logger.getLogger(StartStopTest.class.getName());
45 | public static final String BASE_DIR = getBaseDir();
46 |
47 | public void testRuntime(TestInfo testInfo, File[] searchFiles) throws IOException, InterruptedException {
48 | LOGGER.info("Testing app: " + app.toString() + ", mode: " + mvnCmds.toString());
49 |
50 | File buildLogA = null;
51 | File appDir = new File(BASE_DIR + File.separator + app.dir);
52 | String cn = testInfo.getTestClass().get().getCanonicalName();
53 | String mn = testInfo.getTestMethod().get().getName();
54 | try {
55 | // Cleanup
56 | cleanTarget(app);
57 | Files.createDirectories(Paths.get(appDir.getAbsolutePath() + File.separator + "logs"));
58 |
59 | // Build
60 | buildLogA = new File(appDir.getAbsolutePath() + File.separator + "logs" + File.separator + mvnCmds.name().toLowerCase() + "-build.log");
61 | ExecutorService buildService = Executors.newFixedThreadPool(1);
62 |
63 | List baseBuildCmd = new ArrayList<>();
64 | baseBuildCmd.addAll(Arrays.asList(mvnCmds.mvnCmds[0]));
65 | baseBuildCmd.add("-Dquarkus.version=" + getQuarkusVersion());
66 | baseBuildCmd.add("-Dquarkus.platform.group-id=" + getQuarkusGroupId());
67 | baseBuildCmd.add("-Dquarkus.native.debug.enabled=true");
68 | List cmd = getBuildCommand(baseBuildCmd.toArray(new String[0]));
69 |
70 | LOGGER.info("Building (" + cmd + ")");
71 | buildService.submit(new Commands.ProcessRunner(appDir, buildLogA, cmd, 20));
72 | buildService.shutdown();
73 | buildService.awaitTermination(30, TimeUnit.MINUTES);
74 |
75 | for (File searchFile : searchFiles) {
76 | assertTrue(searchFile.exists(), "File or directory: " + searchFile.getAbsolutePath() + " is missing.");
77 | }
78 | assertTrue(buildLogA.exists());
79 | checkLog(cn, mn, app, mvnCmds, buildLogA);
80 | } finally {
81 | // Archive logs no matter what
82 | archiveLog(cn, mn, buildLogA);
83 | cleanTarget(app);
84 | }
85 | }
86 |
87 | @Test
88 | public void debugSymbolsCheck(TestInfo testInfo) throws IOException, InterruptedException {
89 | testRuntime(testInfo, new File[]{
90 | new File(BASE_DIR + File.separator + app.dir + File.separator + "target" + File.separator + "quarkus-runner.debug"),
91 | new File(BASE_DIR + File.separator + app.dir + File.separator + "target" + File.separator + "quarkus-native-image-source-jar")});
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/OpenTelemetryCollector.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest;
4 | import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse;
5 | import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc;
6 | import io.opentelemetry.proto.trace.v1.Span;
7 | import io.vertx.core.Vertx;
8 | import io.vertx.core.http.HttpServer;
9 | import io.vertx.core.http.HttpServerRequest;
10 | import io.vertx.grpc.common.GrpcStatus;
11 | import io.vertx.grpcio.server.GrpcIoServer;
12 | import org.jboss.logging.Logger;
13 |
14 | import java.io.Closeable;
15 | import java.io.IOException;
16 | import java.util.List;
17 | import java.util.concurrent.atomic.AtomicBoolean;
18 |
19 | /**
20 | * This simplistic collector allows us to test Vert.x-based traces exporter in Quarkus without starting a container.
21 | */
22 | public class OpenTelemetryCollector implements UnitTestResource {
23 | private static final Logger LOGGER = Logger.getLogger(OpenTelemetryCollector.class.getName());
24 | private static final String HELLO_ENDPOINT_OPERATION_NAME = "GET /hello";
25 | private static final String GET_HELLO_INVOCATION_NOT_TRACED = HELLO_ENDPOINT_OPERATION_NAME + " invocation not traced";
26 | /**
27 | * If you change this port, you must also change respective 'quarkus.otel.exporter.otlp.traces.endpoint' value.
28 | */
29 | private static final int OTEL_COLLECTOR_PORT = 4317;
30 | private static final int WEB_ENDPOINT = 16686; // Can be changed to anything, currently is the same as Jaeger.
31 | private static final String GET_HELLO_TRACES_PATH = "/recorded-traces/get-hello";
32 | static final String GET_HELLO_INVOCATION_TRACED = HELLO_ENDPOINT_OPERATION_NAME + " invocation traced";
33 | static final String GET_HELLO_TRACES_URL = "http://localhost:" + WEB_ENDPOINT + GET_HELLO_TRACES_PATH;
34 |
35 | private final Vertx vertx;
36 | private final Closeable backEnd;
37 | private final Closeable frontEnd;
38 | private final AtomicBoolean helloEndpointCallTraced = new AtomicBoolean(false);
39 |
40 |
41 | public OpenTelemetryCollector() {
42 | vertx = Vertx.vertx();
43 | this.backEnd = new GRPCTraceHandler(vertx);
44 | this.frontEnd = new FrontEnd(vertx);
45 | }
46 |
47 | @Override
48 | public void close() throws IOException {
49 | frontEnd.close();
50 | backEnd.close();
51 | vertx.close().toCompletionStage().toCompletableFuture().join();
52 | }
53 |
54 | @Override
55 | public void reset() {
56 | helloEndpointCallTraced.set(false);
57 | }
58 |
59 | private class FrontEnd implements Closeable {
60 | private final HttpServer httpServer;
61 |
62 | public FrontEnd(Vertx vertx) {
63 | httpServer = vertx
64 | .createHttpServer()
65 | .requestHandler(this::handleTracesRequest);
66 | httpServer.listen(WEB_ENDPOINT);
67 | }
68 |
69 | private void handleTracesRequest(HttpServerRequest request) {
70 | final String response;
71 | boolean isTraced = helloEndpointCallTraced.get();
72 | if (isTraced) {
73 | response = GET_HELLO_INVOCATION_TRACED;
74 | } else {
75 | response = GET_HELLO_INVOCATION_NOT_TRACED;
76 | }
77 | request.response().end(response);
78 | }
79 |
80 | @Override
81 | public void close() {
82 | LOGGER.info("Closing the server");
83 | httpServer.close().toCompletionStage().toCompletableFuture().join();
84 | LOGGER.info("The server was closed");
85 | }
86 | }
87 |
88 | class GRPCTraceHandler implements Closeable {
89 | private final HttpServer httpServer;
90 |
91 | public GRPCTraceHandler(Vertx vertx) {
92 | GrpcIoServer grpcHandler = GrpcIoServer.server(vertx);
93 |
94 | // record incoming traces
95 | grpcHandler.callHandler(TraceServiceGrpc.getExportMethod(), request -> {
96 | // https://vertx.io/docs/vertx-grpc/java/#_streaming_request, because Quarkus uses streaming since 3.13
97 | request.handler((ExportTraceServiceRequest tracesRequest) -> {
98 | LOGGER.info("Processing traces");
99 | List traces = tracesRequest.getResourceSpansList().stream()
100 | .flatMap(resourceSpans -> resourceSpans.getScopeSpansList().stream())
101 | .flatMap(scopeSpans -> scopeSpans.getSpansList().stream())
102 | .map(Span::getName)
103 | .toList();
104 |
105 | for (String trace : traces) {
106 | if (trace.contains(HELLO_ENDPOINT_OPERATION_NAME)) {
107 | LOGGER.info("Received trace for " + HELLO_ENDPOINT_OPERATION_NAME);
108 | helloEndpointCallTraced.compareAndSet(false, true);
109 | }
110 | }
111 | });
112 | request.endHandler(v -> {
113 | // https://opentelemetry.io/docs/specs/otlp/#full-success
114 | request.response().end(ExportTraceServiceResponse.newBuilder().build());
115 | });
116 | request.exceptionHandler(err -> { // https://opentelemetry.io/docs/specs/otlp/#failures
117 | request.response().status(GrpcStatus.INVALID_ARGUMENT).end();
118 | });
119 | });
120 | httpServer = vertx
121 | .createHttpServer()
122 | .requestHandler(grpcHandler);
123 | httpServer.listen(OTEL_COLLECTOR_PORT);
124 | LOGGER.info("The listener started!");
125 | }
126 |
127 | @Override
128 | public void close() {
129 | LOGGER.info("Closing the listener");
130 | httpServer.close().toCompletionStage().toCompletableFuture().join();
131 | LOGGER.info("The listener was closed");
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/QuarkusMavenPluginTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop;
2 |
3 | import io.quarkus.ts.startstop.utils.Apps;
4 | import io.quarkus.ts.startstop.utils.Commands;
5 | import org.apache.commons.io.FileUtils;
6 | import org.jboss.logging.Logger;
7 | import org.junit.jupiter.api.Tag;
8 | import org.junit.jupiter.api.Test;
9 | import org.junit.jupiter.api.TestInfo;
10 |
11 | import java.io.File;
12 | import java.io.IOException;
13 | import java.nio.charset.StandardCharsets;
14 | import java.nio.file.Files;
15 | import java.nio.file.Path;
16 | import java.nio.file.Paths;
17 | import java.util.ArrayList;
18 | import java.util.Arrays;
19 | import java.util.Date;
20 | import java.util.List;
21 | import java.util.concurrent.ExecutorService;
22 | import java.util.concurrent.Executors;
23 | import java.util.concurrent.TimeUnit;
24 |
25 | import static io.quarkus.ts.startstop.utils.Commands.getBaseDir;
26 | import static io.quarkus.ts.startstop.utils.Commands.getBuildCommand;
27 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusGroupId;
28 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusVersion;
29 | import static io.quarkus.ts.startstop.utils.Logs.appendln;
30 | import static io.quarkus.ts.startstop.utils.Logs.appendlnSection;
31 | import static io.quarkus.ts.startstop.utils.Logs.archiveLog;
32 | import static io.quarkus.ts.startstop.utils.Logs.writeReport;
33 | import static org.junit.jupiter.api.Assertions.assertTrue;
34 |
35 | @Tag("generator")
36 | public class QuarkusMavenPluginTest {
37 |
38 | private static final Logger LOGGER = Logger.getLogger(StartStopTest.class.getName());
39 | public static final String BASE_DIR = getBaseDir();
40 |
41 | public void runQuarkusMavenPluginGoal(TestInfo testInfo, Apps app, String goalName, String... expectedLogContent) throws IOException, InterruptedException {
42 | LOGGER.info("Testing app: " + app.toString() + ", goal name: " + goalName.toUpperCase());
43 |
44 | File logFile = null;
45 | StringBuilder whatIDidReport = new StringBuilder();
46 | File sourceDir = new File(BASE_DIR + File.separator + app.dir);
47 | File appDestDir = sourceDir
48 | .getParentFile().toPath().toAbsolutePath()
49 | .resolve("target")
50 | .resolve(goalName).toFile();
51 |
52 | String cn = testInfo.getTestClass().get().getCanonicalName();
53 | String mn = testInfo.getTestMethod().get().getName();
54 | try {
55 | // Create a testdir
56 | if (!appDestDir.mkdirs()) {
57 | throw new RuntimeException("Cannot create directory " + appDestDir);
58 | }
59 |
60 | // Copy to path
61 | FileUtils.copyDirectory(sourceDir, appDestDir, true);
62 | LOGGER.info("Copying " + sourceDir +" to the " + appDestDir);
63 | // Cleanup
64 | Files.createDirectories(Paths.get(appDestDir.getAbsolutePath() + File.separator + "logs"));
65 | fixPom(appDestDir, goalName);
66 | // Run quarkus-maven-plugin goal
67 | logFile = new File(appDestDir.getAbsolutePath() + File.separator + "logs" + File.separator + goalName + ".log");
68 | ExecutorService buildService = Executors.newFixedThreadPool(1);
69 |
70 | List baseBuildCmd = new ArrayList<>();
71 | baseBuildCmd.addAll(Arrays.asList("mvn", "clean", "quarkus:" + goalName));
72 | baseBuildCmd.add("-Dquarkus.version=" + getQuarkusVersion());
73 | baseBuildCmd.add("-Dquarkus.platform.group-id=" + getQuarkusGroupId());
74 | List cmd = getBuildCommand(baseBuildCmd.toArray(new String[0]));
75 |
76 | LOGGER.info("Running " + String.join(" ",cmd) +" in " + appDestDir);
77 |
78 | buildService.submit(new Commands.ProcessRunner(appDestDir, logFile, cmd, 5));
79 | appendln(whatIDidReport, "# " + cn + ", " + mn);
80 | appendln(whatIDidReport, (new Date()).toString());
81 | appendln(whatIDidReport, appDestDir.getAbsolutePath());
82 | appendlnSection(whatIDidReport, String.join(" ", cmd));
83 | buildService.shutdown();
84 | buildService.awaitTermination(5, TimeUnit.MINUTES);
85 |
86 | assertTrue(logFile.exists(), "Log file " + logFile + " doesn't exist");
87 |
88 | String logFileContent = Files.readString(logFile.toPath());
89 | assertTrue(logFileContent.contains("BUILD SUCCESS"), "Log file doesn't contain 'BUILD SUCCESS'");
90 |
91 | for (String content : expectedLogContent) {
92 | assertTrue(logFileContent.contains(content), "Log file doesn't contain '" + content + "'");
93 | }
94 |
95 | } finally {
96 | // Archive logs no matter what
97 | archiveLog(cn, mn, logFile);
98 | writeReport(cn, mn, whatIDidReport.toString());
99 | }
100 | }
101 |
102 | private static void fixPom(File workingDir, String name) throws IOException {
103 | Path path = workingDir.toPath().resolve("pom.xml");
104 | String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
105 | content = content
106 | .replaceAll("..", "../..")
107 | .replaceAll("app-jakarta-rest-minimal", "app-jakarta-rest-minimal-" + name);
108 | Files.write(path, content.getBytes(StandardCharsets.UTF_8));
109 | }
110 |
111 | @Test
112 | public void helpTarget(TestInfo testInfo) throws IOException, InterruptedException {
113 | runQuarkusMavenPluginGoal(testInfo, Apps.JAKARTA_REST_MINIMAL, "help", "quarkus:info", "quarkus:update");
114 | }
115 |
116 | @Test
117 | public void infoTarget(TestInfo testInfo) throws IOException, InterruptedException {
118 | runQuarkusMavenPluginGoal(testInfo, Apps.JAKARTA_REST_MINIMAL, "info", "io.quarkus:quarkus-rest");
119 | }
120 |
121 | @Test
122 | public void updateTarget(TestInfo testInfo) throws IOException, InterruptedException {
123 | runQuarkusMavenPluginGoal(testInfo, Apps.JAKARTA_REST_MINIMAL, "update", "quarkus:update goal is experimental");
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/AsyncProfiler.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.jboss.logging.Logger;
5 |
6 | import java.io.File;
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.util.ArrayList;
10 | import java.util.Collections;
11 | import java.util.List;
12 | import java.util.Optional;
13 | import java.util.concurrent.TimeUnit;
14 |
15 | import static io.quarkus.ts.startstop.StartStopTest.BASE_DIR;
16 | import static io.quarkus.ts.startstop.utils.Commands.isThisLinux;
17 | import static io.quarkus.ts.startstop.utils.Commands.runCommand;
18 | import static io.quarkus.ts.startstop.utils.Logs.archiveLog;
19 |
20 | public final class AsyncProfiler {
21 |
22 | private static final Logger LOGGER = Logger.getLogger(AsyncProfiler.class.getName());
23 | private final File lib;
24 | private final File exe;
25 | private final String agentConfig;
26 |
27 | private AsyncProfiler(File lib, File exe, String agentConfig) {
28 | this.lib = lib;
29 | this.exe = exe;
30 | this.agentConfig = agentConfig;
31 | }
32 |
33 | public List createJavaProfiledRunCommand(String[] baseCommand) {
34 | boolean javaCmd = false;
35 | List runCmd = new ArrayList<>(baseCommand.length + 1);
36 | for (String cmdPart : baseCommand) {
37 | runCmd.add(cmdPart);
38 | if (cmdPart.equals(Commands.JAVA_BIN)) {
39 | javaCmd = true;
40 | runCmd.add("-agentpath:" + lib.getAbsolutePath() + "=" + agentConfig);
41 | }
42 | }
43 | if (!javaCmd) {
44 | throw new IllegalArgumentException("No java command found in the base command");
45 | }
46 | return Collections.unmodifiableList(runCmd);
47 | }
48 |
49 | public void stopProfing(File appDir, MvnCmds mvnCmds, Process app, int id) {
50 | try {
51 | File profilingOutputDir = getProfilingOutputDir(appDir);
52 | if (!Files.exists(profilingOutputDir.toPath())) {
53 | Files.createDirectory(profilingOutputDir.toPath());
54 | }
55 | String profilerOutputFullPath = profilingOutputDir.getAbsolutePath() + File.separator + mvnCmds.name().toLowerCase() + "-run-" + id + ".html";
56 | LOGGER.infof("Attaching profiler agent to the JVM process. Output HTML: %s", profilerOutputFullPath);
57 | LOGGER.info("Stopping the profiler agent...");
58 | Process stopProfiling = runCommand(List.of(exe.getAbsolutePath(), "stop", "-f", profilerOutputFullPath, "" + app.pid()), appDir, null);
59 | long initiateStopAt = stopProfiling.info().startInstant().get().toEpochMilli();
60 | if (stopProfiling.isAlive() && !stopProfiling.waitFor(10, TimeUnit.SECONDS)) {
61 | LOGGER.warn("Profiler agent did not stop in 10 seconds");
62 | } else {
63 | final long stoppingDuration = System.currentTimeMillis() - initiateStopAt;
64 | final long cpuTimeMs = app.info().totalCpuDuration().get().toMillis();
65 | LOGGER.infof("CPU time of the profiled process: %d ms", cpuTimeMs);
66 | LOGGER.infof("Stopping the profiler agent tooks %d ms", stoppingDuration);
67 | if (stopProfiling.exitValue() != 0) {
68 | LOGGER.warn("Profiler agent did not stop successfully: exitValue = " + stopProfiling.exitValue());
69 | }
70 | }
71 | } catch (Throwable t) {
72 | LOGGER.error("Failed to stop the profiler agent", t);
73 | throw new RuntimeException(t);
74 | }
75 | }
76 |
77 | public void archiveProfilingResults(String cn, String mn, File appDir) {
78 | File profilingOutputDir = getProfilingOutputDir(appDir);
79 | if (!Files.exists(profilingOutputDir.toPath())) {
80 | return;
81 | }
82 | try {
83 | archiveLog(cn, mn, profilingOutputDir);
84 | } catch (IOException e) {
85 | throw new RuntimeException(e);
86 | }
87 | }
88 |
89 | public static void cleanProfilingResults(Apps app) {
90 | String profiling = BASE_DIR + File.separator + app.dir + File.separator + "profiling";
91 | Commands.cleanDirOrFile(profiling);
92 | }
93 |
94 | private static File getProfilingOutputDir(File appDir) {
95 | File profilingOutputDir = new File(appDir.getAbsolutePath() + File.separator + "profiling");
96 | return profilingOutputDir;
97 | }
98 |
99 | public static Optional create() {
100 | if (!isThisLinux) {
101 | return Optional.empty();
102 | }
103 | String apDirFullPath = getAsyncProfilerDir();
104 | if (apDirFullPath == null) {
105 | return Optional.empty();
106 | }
107 | File apDir = new File(apDirFullPath);
108 | if (!apDir.isDirectory()) {
109 | throw new IllegalArgumentException(apDirFullPath + " is not a directory");
110 | }
111 | // check if the bin/asProf and lib/libasyncProfiler.so are present
112 | File asProf = new File(apDir, "bin" + File.separator + "asprof");
113 | if (!asProf.exists() || !asProf.canExecute() || !asProf.isFile()) {
114 | throw new IllegalStateException("asprof executable not found or not executable on : " + asProf.getAbsolutePath());
115 | }
116 | File soLib = new File(apDir, "lib" + File.separator + "libasyncProfiler.so");
117 | if (!soLib.exists() || !soLib.isFile()) {
118 | throw new IllegalStateException("libasyncProfiler.so not found on : " + soLib.getAbsolutePath());
119 | }
120 | return Optional.of(new AsyncProfiler(soLib, asProf, getAsyncProfilerAgentConfig()));
121 | }
122 |
123 | private static String getAsyncProfilerAgentConfig() {
124 | String apConfig = System.getenv().get("ASYNC_PROFILER_AGENT_CONFIG");
125 | if (StringUtils.isNotBlank(apConfig)) {
126 | return apConfig;
127 | }
128 | apConfig = System.getProperty("ASYNC_PROFILER_AGENT_CONFIG");
129 | if (StringUtils.isNotBlank(apConfig)) {
130 | return apConfig;
131 | }
132 | return "start,event=cpu,interval=1000000";
133 | }
134 |
135 | private static String getAsyncProfilerDir() {
136 | String apLibDir = System.getenv().get("ASYNC_PROFILER_DIR");
137 | if (StringUtils.isNotBlank(apLibDir)) {
138 | return apLibDir;
139 | }
140 | apLibDir = System.getProperty("ASYNC_PROFILER_DIR");
141 | if (StringUtils.isNotBlank(apLibDir)) {
142 | return apLibDir;
143 | }
144 | return null;
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/utils/LogBuilder.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.utils;
2 |
3 | import java.util.Objects;
4 |
5 | public class LogBuilder {
6 |
7 | public static class Log {
8 | public final String headerCSV;
9 | public final String headerMarkdown;
10 | public final String lineCSV;
11 | public final String lineMarkdown;
12 |
13 | public Log(String headerCSV, String headerMarkdown, String lineCSV, String lineMarkdown) {
14 | this.headerCSV = headerCSV;
15 | this.headerMarkdown = headerMarkdown;
16 | this.lineCSV = lineCSV;
17 | this.lineMarkdown = lineMarkdown;
18 | }
19 | }
20 |
21 | private static final String buildTimeMsHeader = "buildTimeMs";
22 | private long buildTimeMs = -1L;
23 | private static final String timeToFirstOKRequestMsHeader = "timeToFirstOKRequestMs";
24 | private long timeToFirstOKRequestMs = -1L;
25 | private static final String timeToReloadedOKRequestHeader = "timeToReloadMs";
26 | private long timeToReloadedOKRequest = -1L;
27 | private static final String startedInMsHeader = "startedInMs";
28 | private long startedInMs = -1L;
29 | private static final String stoppedInMsHeader = "stoppedInMs";
30 | private long stoppedInMs = -1L;
31 | private static final String rssKbHeader = "RSSkB";
32 | private long rssKb = -1L;
33 | private static final String openedFilesHeader = "FDs";
34 | private long openedFiles = -1L;
35 | private static final String appHeader = "App";
36 | private Apps app = null;
37 | private static final String modeHeader = "Mode";
38 | private MvnCmds mode = null;
39 |
40 | public LogBuilder buildTimeMs(long buildTimeMs) {
41 | if (buildTimeMs <= 0) {
42 | throw new IllegalArgumentException("buildTimeMs must be a positive long, was: " + buildTimeMs);
43 | }
44 | this.buildTimeMs = buildTimeMs;
45 | return this;
46 | }
47 |
48 | public LogBuilder timeToFirstOKRequestMs(long timeToFirstOKRequestMs) {
49 | if (timeToFirstOKRequestMs <= 0) {
50 | throw new IllegalArgumentException("timeToFirstOKRequestMs must be a positive long, was: " + timeToFirstOKRequestMs);
51 | }
52 | this.timeToFirstOKRequestMs = timeToFirstOKRequestMs;
53 | return this;
54 | }
55 |
56 | public LogBuilder timeToReloadedOKRequest(long timeToReloadedOKRequest) {
57 | if (timeToReloadedOKRequest <= 0) {
58 | throw new IllegalArgumentException("timeToReloadedOKRequest must be a positive long, was: " + timeToFirstOKRequestMs);
59 | }
60 | this.timeToReloadedOKRequest = timeToReloadedOKRequest;
61 | return this;
62 | }
63 |
64 | public LogBuilder startedInMs(long startedInMs) {
65 | if (startedInMs <= 0) {
66 | throw new IllegalArgumentException("startedInMs must be a positive long, was: " + startedInMs);
67 | }
68 | this.startedInMs = startedInMs;
69 | return this;
70 | }
71 |
72 | public LogBuilder stoppedInMs(long stoppedInMs) {
73 | // Quarkus is not being gratefully shutdown in Windows when running in Dev mode.
74 | // Reported by https://github.com/quarkusio/quarkus/issues/14647.
75 | if (stoppedInMs <= 0 && Commands.isThisWindows) {
76 | // do nothing in Windows
77 | return this;
78 | }
79 |
80 | if (stoppedInMs < 0) {
81 | throw new IllegalArgumentException("stoppedInMs must be a positive long or 0, was: " + stoppedInMs);
82 | }
83 | this.stoppedInMs = stoppedInMs;
84 | return this;
85 | }
86 |
87 | public LogBuilder rssKb(long rssKb) {
88 | if (rssKb <= 0) {
89 | throw new IllegalArgumentException("rssKb must be a positive long, was: " + rssKb);
90 | }
91 | this.rssKb = rssKb;
92 | return this;
93 | }
94 |
95 | public LogBuilder openedFiles(long openedFiles) {
96 | if (openedFiles <= 0) {
97 | throw new IllegalArgumentException("openedFiles must be a positive long, was: " + openedFiles);
98 | }
99 | this.openedFiles = openedFiles;
100 | return this;
101 | }
102 |
103 | public LogBuilder app(Apps app) {
104 | Objects.requireNonNull(app, "Valid app flavour must be provided");
105 | this.app = app;
106 | return this;
107 | }
108 |
109 | public LogBuilder mode(MvnCmds mode) {
110 | Objects.requireNonNull(mode, "Valid app flavour must be provided");
111 | this.mode = mode;
112 | return this;
113 | }
114 |
115 | public Log build() {
116 | StringBuilder h = new StringBuilder(512);
117 | StringBuilder l = new StringBuilder(512);
118 | int sections = 0;
119 | if (app != null) {
120 | h.append(appHeader);
121 | h.append(',');
122 | l.append(app);
123 | l.append(',');
124 | sections++;
125 | }
126 | if (mode != null) {
127 | h.append(modeHeader);
128 | h.append(',');
129 | l.append(mode);
130 | l.append(',');
131 | sections++;
132 | }
133 | if (buildTimeMs != -1L) {
134 | h.append(buildTimeMsHeader);
135 | h.append(',');
136 | l.append(buildTimeMs);
137 | l.append(',');
138 | sections++;
139 | }
140 | if (timeToFirstOKRequestMs != -1L) {
141 | h.append(timeToFirstOKRequestMsHeader);
142 | h.append(',');
143 | l.append(timeToFirstOKRequestMs);
144 | l.append(',');
145 | sections++;
146 | }
147 | if (timeToReloadedOKRequest != -1L) {
148 | h.append(timeToReloadedOKRequestHeader);
149 | h.append(',');
150 | l.append(timeToReloadedOKRequest);
151 | l.append(',');
152 | sections++;
153 | }
154 | if (startedInMs != -1L) {
155 | h.append(startedInMsHeader);
156 | h.append(',');
157 | l.append(startedInMs);
158 | l.append(',');
159 | sections++;
160 | }
161 | if (stoppedInMs != -1L) {
162 | h.append(stoppedInMsHeader);
163 | h.append(',');
164 | l.append(stoppedInMs);
165 | l.append(',');
166 | sections++;
167 | }
168 | if (rssKb != -1L) {
169 | h.append(rssKbHeader);
170 | h.append(',');
171 | l.append(rssKb);
172 | l.append(',');
173 | sections++;
174 | }
175 | if (openedFiles != -1L) {
176 | h.append(openedFilesHeader);
177 | h.append(',');
178 | l.append(openedFiles);
179 | l.append(',');
180 | sections++;
181 | }
182 | String header = h.toString();
183 | // Strip trailing ',' for CSV
184 | String headerCSV = header.substring(0, header.length() - 1);
185 | String headerMarkdown = "|" + header.replaceAll(",", "|") + "\n|" + " --- |".repeat(sections);
186 | String line = l.toString();
187 | String lineCSV = line.substring(0, line.length() - 1);
188 | String lineMarkdown = "|" + line.replaceAll(",", "|");
189 | return new Log(headerCSV, headerMarkdown, lineCSV, lineMarkdown);
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | <# : batch portion
2 | @REM ----------------------------------------------------------------------------
3 | @REM Licensed to the Apache Software Foundation (ASF) under one
4 | @REM or more contributor license agreements. See the NOTICE file
5 | @REM distributed with this work for additional information
6 | @REM regarding copyright ownership. The ASF licenses this file
7 | @REM to you under the Apache License, Version 2.0 (the
8 | @REM "License"); you may not use this file except in compliance
9 | @REM with the License. You may obtain a copy of the License at
10 | @REM
11 | @REM http://www.apache.org/licenses/LICENSE-2.0
12 | @REM
13 | @REM Unless required by applicable law or agreed to in writing,
14 | @REM software distributed under the License is distributed on an
15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | @REM KIND, either express or implied. See the License for the
17 | @REM specific language governing permissions and limitations
18 | @REM under the License.
19 | @REM ----------------------------------------------------------------------------
20 |
21 | @REM ----------------------------------------------------------------------------
22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2
23 | @REM
24 | @REM Optional ENV vars
25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution
26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
28 | @REM ----------------------------------------------------------------------------
29 |
30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
31 | @SET __MVNW_CMD__=
32 | @SET __MVNW_ERROR__=
33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
34 | @SET PSModulePath=
35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
37 | )
38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
39 | @SET __MVNW_PSMODULEP_SAVE=
40 | @SET __MVNW_ARG0_NAME__=
41 | @SET MVNW_USERNAME=
42 | @SET MVNW_PASSWORD=
43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
44 | @echo Cannot start maven from wrapper >&2 && exit /b 1
45 | @GOTO :EOF
46 | : end batch / begin powershell #>
47 |
48 | $ErrorActionPreference = "Stop"
49 | if ($env:MVNW_VERBOSE -eq "true") {
50 | $VerbosePreference = "Continue"
51 | }
52 |
53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
55 | if (!$distributionUrl) {
56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
57 | }
58 |
59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
60 | "maven-mvnd-*" {
61 | $USE_MVND = $true
62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
63 | $MVN_CMD = "mvnd.cmd"
64 | break
65 | }
66 | default {
67 | $USE_MVND = $false
68 | $MVN_CMD = $script -replace '^mvnw','mvn'
69 | break
70 | }
71 | }
72 |
73 | # apply MVNW_REPOURL and calculate MAVEN_HOME
74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
75 | if ($env:MVNW_REPOURL) {
76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
78 | }
79 | $distributionUrlName = $distributionUrl -replace '^.*/',''
80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
82 | if ($env:MAVEN_USER_HOME) {
83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
84 | }
85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
87 |
88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
91 | exit $?
92 | }
93 |
94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
96 | }
97 |
98 | # prepare tmp dir
99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
102 | trap {
103 | if ($TMP_DOWNLOAD_DIR.Exists) {
104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
106 | }
107 | }
108 |
109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
110 |
111 | # Download and Install Apache Maven
112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
113 | Write-Verbose "Downloading from: $distributionUrl"
114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
115 |
116 | $webclient = New-Object System.Net.WebClient
117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
119 | }
120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
122 |
123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
125 | if ($distributionSha256Sum) {
126 | if ($USE_MVND) {
127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
128 | }
129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
132 | }
133 | }
134 |
135 | # unzip and move
136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
138 | try {
139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
140 | } catch {
141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
142 | Write-Error "fail to move MAVEN_HOME"
143 | }
144 | } finally {
145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
147 | }
148 |
149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
150 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: "StartStop CI"
2 | on:
3 | pull_request:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0 22 * * *'
7 | jobs:
8 | linux-validate-format:
9 | name: Linux - Validate format
10 | runs-on: ubuntu-latest
11 | strategy:
12 | matrix:
13 | java: [ 17 ]
14 | steps:
15 | - uses: actions/checkout@v5
16 | - name: Install JDK {{ matrix.java }}
17 | uses: actions/setup-java@v5
18 | with:
19 | distribution: 'temurin'
20 | java-version: ${{ matrix.java }}
21 | check-latest: true
22 | cache: 'maven'
23 | - name: Build with Maven
24 | run: |
25 | mvn -V -B --no-transfer-progress -s .github/mvn-settings.xml validate -Pide,validate-format
26 | linux-build-released-jvm:
27 | name: Linux - JVM build
28 | runs-on: ubuntu-latest
29 | strategy:
30 | matrix:
31 | java: [ 17, 21 ]
32 | steps:
33 | - uses: actions/checkout@v5
34 | - name: Install JDK {{ matrix.java }}
35 | uses: actions/setup-java@v5
36 | with:
37 | distribution: 'temurin'
38 | java-version: ${{ matrix.java }}
39 | check-latest: true
40 | cache: 'maven'
41 | - name: Build with Maven
42 | run: |
43 | mvn -V -B --no-transfer-progress -s .github/mvn-settings.xml clean verify -DexcludeTags=product,native,codequarkus -Dgh.actions
44 | - name: Zip Artifacts
45 | if: failure()
46 | run: |
47 | zip -r artifacts-jvm${{ matrix.java }}.zip . -i '*-reports/*' '*/archived-logs/*'
48 | - name: Archive artifacts
49 | uses: actions/upload-artifact@v4
50 | if: failure()
51 | with:
52 | name: ci-artifacts
53 | path: artifacts-jvm${{ matrix.java }}.zip
54 | linux-build-released-native:
55 | name: Linux - Native build
56 | runs-on: ubuntu-latest
57 | strategy:
58 | matrix:
59 | java: [ 17 ]
60 | steps:
61 | - uses: actions/checkout@v5
62 | - name: Install JDK {{ matrix.java }}
63 | uses: actions/setup-java@v5
64 | with:
65 | distribution: 'temurin'
66 | java-version: ${{ matrix.java }}
67 | check-latest: true
68 | cache: 'maven'
69 | - name: Build with Maven
70 | run: |
71 | mvn -V -B --no-transfer-progress -s .github/mvn-settings.xml clean verify -DincludeTags="native" -DexcludeTags=product,codequarkus -Dquarkus.native.container-build=true -Dquarkus.native.container-runtime=docker -Dgh.actions
72 | - name: Zip Artifacts
73 | if: failure()
74 | run: |
75 | zip -r artifacts-native${{ matrix.java }}.zip . -i '*-reports/*' '*/archived-logs/*'
76 | - name: Archive artifacts
77 | uses: actions/upload-artifact@v4
78 | if: failure()
79 | with:
80 | name: ci-artifacts
81 | path: artifacts-native${{ matrix.java }}.zip
82 | linux-build-code-start-redhat:
83 | name: Linux - RedHat Code Quarkus
84 | runs-on: ubuntu-latest
85 | strategy:
86 | matrix:
87 | java: [ 17, 21 ]
88 | steps:
89 | - uses: actions/checkout@v5
90 | - name: Install JDK {{ matrix.java }}
91 | uses: actions/setup-java@v5
92 | with:
93 | distribution: 'temurin'
94 | java-version: ${{ matrix.java }}
95 | check-latest: true
96 | cache: 'maven'
97 | - name: Build with Maven
98 | run: |
99 | mvn -V -B --no-transfer-progress -s .github/mvn-settings.xml clean verify -Ptestsuite -DincludeTags=codequarkus -Dgh.actions
100 | - name: Zip Artifacts
101 | if: failure()
102 | run: |
103 | zip -r artifacts-code-start${{ matrix.java }}.zip . -i '*-reports/*' '*/archived-logs/*'
104 | - name: Archive artifacts
105 | uses: actions/upload-artifact@v4
106 | if: failure()
107 | with:
108 | name: ci-artifacts
109 | path: artifacts-redhat-code-start${{ matrix.java }}.zip
110 | linux-build-code-start-ibm:
111 | name: Linux - IBM Code Quarkus
112 | runs-on: ubuntu-latest
113 | strategy:
114 | matrix:
115 | java: [ 21 ]
116 | steps:
117 | - uses: actions/checkout@v5
118 | - name: Install JDK {{ matrix.java }}
119 | uses: actions/setup-java@v5
120 | with:
121 | distribution: 'temurin'
122 | java-version: ${{ matrix.java }}
123 | check-latest: true
124 | cache: 'maven'
125 | - name: Build with Maven
126 | run: |
127 | mvn -V -B --no-transfer-progress -s .github/mvn-settings.xml clean verify -Ptestsuite,ibm -DincludeTags=codequarkus -Dgh.actions
128 | - name: Zip Artifacts
129 | if: failure()
130 | run: |
131 | zip -r artifacts-code-start${{ matrix.java }}.zip . -i '*-reports/*' '*/archived-logs/*'
132 | - name: Archive artifacts
133 | uses: actions/upload-artifact@v4
134 | if: failure()
135 | with:
136 | name: ci-artifacts
137 | path: artifacts-ibm-code-start${{ matrix.java }}.zip
138 | windows-build-released-jvm:
139 | name: Windows - JVM build
140 | runs-on: windows-latest
141 | strategy:
142 | matrix:
143 | java: [ 17, 21 ]
144 | steps:
145 | - uses: actions/checkout@v5
146 | - name: Install JDK {{ matrix.java }}
147 | uses: actions/setup-java@v5
148 | with:
149 | distribution: 'temurin'
150 | java-version: ${{ matrix.java }}
151 | check-latest: true
152 | cache: 'maven'
153 | - name: Build with Maven
154 | shell: bash
155 | run: |
156 | mvn -V -B --no-transfer-progress -s .github/mvn-settings.xml clean verify -DexcludeTags='product,native,codequarkus' -Dgh.actions
157 | - name: Zip Artifacts
158 | if: failure()
159 | shell: bash
160 | run: |
161 | # Disambiguate windows find from cygwin find
162 | /usr/bin/find . -name '*-reports' -o -name 'archived-logs' -type d | tar -czf artifacts-windows-jvm${{ matrix.java }}.tar -T -
163 | - name: Archive artifacts
164 | uses: actions/upload-artifact@v4
165 | if: failure()
166 | with:
167 | name: ci-artifacts
168 | path: artifacts-windows-jvm${{ matrix.java }}.tar
169 | windows-build-released-native:
170 | name: Windows - Native build
171 | runs-on: windows-latest
172 | strategy:
173 | matrix:
174 | java: [ 17 ]
175 | graalvm-version: [ "mandrel-latest" ]
176 | graalvm-java-version: [ "21" ]
177 | steps:
178 | - uses: actions/checkout@v5
179 | - name: Install JDK {{ matrix.java }}
180 | uses: actions/setup-java@v5
181 | with:
182 | distribution: 'temurin'
183 | java-version: ${{ matrix.java }}
184 | check-latest: true
185 | cache: 'maven'
186 | - name: Setup GraalVM
187 | id: setup-graalvm
188 | uses: graalvm/setup-graalvm@v1
189 | with:
190 | version: ${{ matrix.graalvm-version }}
191 | java-version: ${{ matrix.graalvm-java-version }}
192 | github-token: ${{ secrets.GITHUB_TOKEN }}
193 | - name: Configure Pagefile
194 | # Increased the page-file size due to memory-consumption of native-image command
195 | # For details see https://github.com/actions/virtual-environments/issues/785
196 | uses: al-cheb/configure-pagefile-action@v1.4
197 | - name: Build with Maven
198 | run: |
199 | mvn -V -B --no-transfer-progress -s .github/mvn-settings.xml clean verify -DincludeTags="native" -DexcludeTags="product,codequarkus" -Dquarkus.native.native-image-xmx=6g -Dgh.actions
200 | shell: cmd
201 | - name: Zip Artifacts
202 | if: failure()
203 | shell: bash
204 | run: |
205 | # Disambiguate windows find from cygwin find
206 | /usr/bin/find . -name '*-reports' -o -name 'archived-logs' -type d | tar -czf artifacts-windows-native${{ matrix.java }}.tar -T -
207 | - name: Archive artifacts
208 | uses: actions/upload-artifact@v4
209 | if: failure()
210 | with:
211 | name: ci-artifacts
212 | path: artifacts-windows-native${{ matrix.java }}.tar
213 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | io.quarkus.ts.startstop
5 | parent
6 | 1.0.0-SNAPSHOT
7 | pom
8 | Quarkus StartStop TS: Parent
9 |
10 | 3.30.4
11 | io.quarkus.platform
12 | 3.30.4
13 | 17
14 | 17
15 | 17
16 | 3.14.1
17 | 3.5.4
18 | 3.5.4
19 | 3.6.1
20 | UTF-8
21 | 6.0.1
22 | 2.21.0
23 | 3.20.0
24 | 3.6.1.Final
25 | 2.25.3
26 | 1.57.0
27 | 5.0.6
28 | 2.29.0
29 | 1.12.0
30 | 4.1.0
31 | 1.9.0-alpha
32 | 2.5
33 |
34 | generator,startstop,bomtests,codequarkus,special-chars
35 |
36 | **/*Ibm*Test.java
37 |
38 | format
39 | sort
40 | xml-format
41 |
42 |
43 |
44 | org.jboss.logging
45 | jboss-logging
46 | ${jboss-logging.version}
47 | test
48 |
49 |
50 | org.apache.logging.log4j
51 | log4j-api
52 | ${log4j.version}
53 | test
54 |
55 |
56 | org.apache.logging.log4j
57 | log4j-core
58 | ${log4j.version}
59 | test
60 |
61 |
62 | com.microsoft.playwright
63 | playwright
64 | ${playwright.version}
65 | test
66 |
67 |
68 | io.opentelemetry.proto
69 | opentelemetry-proto
70 | ${opentelemetry-proto.version}
71 | test
72 |
73 |
74 |
75 |
76 | testsuite
77 |
78 | testsuite
79 |
80 |
81 | none
82 |
83 |
84 |
85 | testsuite-no-native
86 |
87 | testsuite
88 |
89 |
90 | native
91 |
92 |
93 |
94 | testsuite-community
95 |
96 | testsuite
97 |
98 |
99 | product
100 |
101 |
102 |
103 | testsuite-community-no-native
104 |
105 | testsuite
106 |
107 |
108 | product,native
109 |
110 |
111 |
112 | ide
113 |
114 | true
115 |
116 |
117 | none
118 |
119 |
120 | testsuite
121 | app-jakarta-rest-minimal
122 | app-full-microprofile
123 |
124 |
125 |
126 | validate-format
127 |
128 |
129 | validate-format
130 |
131 |
132 |
133 | validate
134 | check
135 | xml-check
136 |
137 |
138 |
139 | ibm
140 |
141 | **/*RedHat*Test.java
142 |
143 |
144 |
145 | no-exclusion
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | net.revelc.code.formatter
155 | formatter-maven-plugin
156 | ${formatter-maven-plugin.version}
157 |
158 |
159 |
160 | ${src.format.goal}
161 |
162 |
163 |
164 |
165 |
166 | quarkus-ide-config
167 | io.quarkus
168 | ${quarkus-ide-config.version}
169 |
170 |
171 |
172 | eclipse-format.xml
173 | LF
174 |
175 |
176 |
177 | net.revelc.code
178 | impsort-maven-plugin
179 | ${impsort-maven-plugin.version}
180 |
181 |
182 | .cache
183 | java.,javax.,org.,com.
184 | *
185 | true
186 |
187 |
188 |
189 |
190 | ${src.sort.goal}
191 |
192 |
193 |
194 |
195 |
196 | au.com.acegi
197 | xml-format-maven-plugin
198 | ${xml-format-maven-plugin}
199 |
200 |
201 |
202 | ${xml.format.goal}
203 |
204 |
205 | 4
206 |
207 |
208 | **/target/**/*.xml
209 |
210 | **/quarkus/**/*.xml
211 |
212 | .idea/**
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.3.2
23 | #
24 | # Optional ENV vars
25 | # -----------------
26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source
27 | # MVNW_REPOURL - repo url base for downloading maven distribution
28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
30 | # ----------------------------------------------------------------------------
31 |
32 | set -euf
33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x
34 |
35 | # OS specific support.
36 | native_path() { printf %s\\n "$1"; }
37 | case "$(uname)" in
38 | CYGWIN* | MINGW*)
39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
40 | native_path() { cygpath --path --windows "$1"; }
41 | ;;
42 | esac
43 |
44 | # set JAVACMD and JAVACCMD
45 | set_java_home() {
46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
47 | if [ -n "${JAVA_HOME-}" ]; then
48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
49 | # IBM's JDK on AIX uses strange locations for the executables
50 | JAVACMD="$JAVA_HOME/jre/sh/java"
51 | JAVACCMD="$JAVA_HOME/jre/sh/javac"
52 | else
53 | JAVACMD="$JAVA_HOME/bin/java"
54 | JAVACCMD="$JAVA_HOME/bin/javac"
55 |
56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
59 | return 1
60 | fi
61 | fi
62 | else
63 | JAVACMD="$(
64 | 'set' +e
65 | 'unset' -f command 2>/dev/null
66 | 'command' -v java
67 | )" || :
68 | JAVACCMD="$(
69 | 'set' +e
70 | 'unset' -f command 2>/dev/null
71 | 'command' -v javac
72 | )" || :
73 |
74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
76 | return 1
77 | fi
78 | fi
79 | }
80 |
81 | # hash string like Java String::hashCode
82 | hash_string() {
83 | str="${1:-}" h=0
84 | while [ -n "$str" ]; do
85 | char="${str%"${str#?}"}"
86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
87 | str="${str#?}"
88 | done
89 | printf %x\\n $h
90 | }
91 |
92 | verbose() { :; }
93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
94 |
95 | die() {
96 | printf %s\\n "$1" >&2
97 | exit 1
98 | }
99 |
100 | trim() {
101 | # MWRAPPER-139:
102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
103 | # Needed for removing poorly interpreted newline sequences when running in more
104 | # exotic environments such as mingw bash on Windows.
105 | printf "%s" "${1}" | tr -d '[:space:]'
106 | }
107 |
108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
109 | while IFS="=" read -r key value; do
110 | case "${key-}" in
111 | distributionUrl) distributionUrl=$(trim "${value-}") ;;
112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
113 | esac
114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
116 |
117 | case "${distributionUrl##*/}" in
118 | maven-mvnd-*bin.*)
119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
125 | *)
126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
127 | distributionPlatform=linux-amd64
128 | ;;
129 | esac
130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
131 | ;;
132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
134 | esac
135 |
136 | # apply MVNW_REPOURL and calculate MAVEN_HOME
137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/
138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
139 | distributionUrlName="${distributionUrl##*/}"
140 | distributionUrlNameMain="${distributionUrlName%.*}"
141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}"
142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
144 |
145 | exec_maven() {
146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
148 | }
149 |
150 | if [ -d "$MAVEN_HOME" ]; then
151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME"
152 | exec_maven "$@"
153 | fi
154 |
155 | case "${distributionUrl-}" in
156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
158 | esac
159 |
160 | # prepare tmp dir
161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
163 | trap clean HUP INT TERM EXIT
164 | else
165 | die "cannot create temp dir"
166 | fi
167 |
168 | mkdir -p -- "${MAVEN_HOME%/*}"
169 |
170 | # Download and Install Apache Maven
171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
172 | verbose "Downloading from: $distributionUrl"
173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
174 |
175 | # select .zip or .tar.gz
176 | if ! command -v unzip >/dev/null; then
177 | distributionUrl="${distributionUrl%.zip}.tar.gz"
178 | distributionUrlName="${distributionUrl##*/}"
179 | fi
180 |
181 | # verbose opt
182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
184 |
185 | # normalize http auth
186 | case "${MVNW_PASSWORD:+has-password}" in
187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
189 | esac
190 |
191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
192 | verbose "Found wget ... using wget"
193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
195 | verbose "Found curl ... using curl"
196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
197 | elif set_java_home; then
198 | verbose "Falling back to use Java to download"
199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
201 | cat >"$javaSource" <<-END
202 | public class Downloader extends java.net.Authenticator
203 | {
204 | protected java.net.PasswordAuthentication getPasswordAuthentication()
205 | {
206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
207 | }
208 | public static void main( String[] args ) throws Exception
209 | {
210 | setDefault( new Downloader() );
211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
212 | }
213 | }
214 | END
215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java
216 | verbose " - Compiling Downloader.java ..."
217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
218 | verbose " - Running Downloader.java ..."
219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
220 | fi
221 |
222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file
223 | if [ -n "${distributionSha256Sum-}" ]; then
224 | distributionSha256Result=false
225 | if [ "$MVN_CMD" = mvnd.sh ]; then
226 | echo "Checksum validation is not supported for maven-mvnd." >&2
227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
228 | exit 1
229 | elif command -v sha256sum >/dev/null; then
230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
231 | distributionSha256Result=true
232 | fi
233 | elif command -v shasum >/dev/null; then
234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
235 | distributionSha256Result=true
236 | fi
237 | else
238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
240 | exit 1
241 | fi
242 | if [ $distributionSha256Result = false ]; then
243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
245 | exit 1
246 | fi
247 | fi
248 |
249 | # unzip and move
250 | if command -v unzip >/dev/null; then
251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
252 | else
253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
254 | fi
255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
257 |
258 | clean || :
259 | exec_maven "$@"
260 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/ArtifactGeneratorBOMTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop;
2 |
3 | import io.quarkus.ts.startstop.utils.Apps;
4 | import io.quarkus.ts.startstop.utils.Commands;
5 | import io.quarkus.ts.startstop.utils.MvnCmds;
6 | import io.quarkus.ts.startstop.utils.TestFlags;
7 | import io.quarkus.ts.startstop.utils.URLContent;
8 | import io.quarkus.ts.startstop.utils.WebpageTester;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.jboss.logging.Logger;
11 | import org.junit.jupiter.api.Disabled;
12 | import org.junit.jupiter.api.Tag;
13 | import org.junit.jupiter.api.Test;
14 | import org.junit.jupiter.api.TestInfo;
15 |
16 | import java.io.File;
17 | import java.nio.file.Files;
18 | import java.nio.file.Paths;
19 | import java.util.Date;
20 | import java.util.EnumSet;
21 | import java.util.List;
22 | import java.util.Set;
23 | import java.util.concurrent.ExecutorService;
24 | import java.util.concurrent.Executors;
25 | import java.util.concurrent.TimeUnit;
26 |
27 | import static io.quarkus.ts.startstop.ArtifactGeneratorTest.langchain4jExtensions;
28 | import static io.quarkus.ts.startstop.ArtifactGeneratorTest.supportedExtensionsSubsetSetA;
29 | import static io.quarkus.ts.startstop.ArtifactGeneratorTest.supportedExtensionsSubsetSetB;
30 | import static io.quarkus.ts.startstop.ArtifactGeneratorTest.supportedReactiveExtensionsSubsetSetA;
31 | import static io.quarkus.ts.startstop.ArtifactGeneratorTest.supportedReactiveExtensionsSubsetSetB;
32 | import static io.quarkus.ts.startstop.utils.Commands.adjustPrettyPrintForJsonLogging;
33 | import static io.quarkus.ts.startstop.utils.Commands.cleanDirOrFile;
34 | import static io.quarkus.ts.startstop.utils.Commands.confAppPropsForSkeleton;
35 | import static io.quarkus.ts.startstop.utils.Commands.confIndexPageForSkeleton;
36 | import static io.quarkus.ts.startstop.utils.Commands.dropEntityAnnotations;
37 | import static io.quarkus.ts.startstop.utils.Commands.getArtifactGeneBaseDir;
38 | import static io.quarkus.ts.startstop.utils.Commands.getBuildCommand;
39 | import static io.quarkus.ts.startstop.utils.Commands.getGeneratorCommand;
40 | import static io.quarkus.ts.startstop.utils.Commands.getLocalMavenRepoDir;
41 | import static io.quarkus.ts.startstop.utils.Commands.getRunCommand;
42 | import static io.quarkus.ts.startstop.utils.Commands.parsePort;
43 | import static io.quarkus.ts.startstop.utils.Commands.processStopper;
44 | import static io.quarkus.ts.startstop.utils.Commands.removeRepositoriesAndPluginRepositories;
45 | import static io.quarkus.ts.startstop.utils.Commands.runCommand;
46 | import static io.quarkus.ts.startstop.utils.Commands.waitForTcpClosed;
47 | import static io.quarkus.ts.startstop.utils.Logs.appendln;
48 | import static io.quarkus.ts.startstop.utils.Logs.appendlnSection;
49 | import static io.quarkus.ts.startstop.utils.Logs.archiveLog;
50 | import static io.quarkus.ts.startstop.utils.Logs.checkJarSuffixes;
51 | import static io.quarkus.ts.startstop.utils.Logs.checkLog;
52 | import static io.quarkus.ts.startstop.utils.Logs.writeReport;
53 | import static org.junit.jupiter.api.Assertions.assertTrue;
54 |
55 | /**
56 | * BOM tests for quarkus-maven-plugin generator, command defines BOM via platformArtifactId property
57 | */
58 | @Tag("bomtests")
59 | public class ArtifactGeneratorBOMTest {
60 |
61 | private static final Logger LOGGER = Logger.getLogger(ArtifactGeneratorBOMTest.class.getName());
62 |
63 | public void testRuntime(TestInfo testInfo, String[] extensions, Set flags) throws Exception {
64 | Process pA = null;
65 | File generateLog = null;
66 | File buildLogA = null;
67 | File runLogA = null;
68 | StringBuilder whatIDidReport = new StringBuilder();
69 | String cn = testInfo.getTestClass().get().getCanonicalName();
70 | String mn = testInfo.getTestMethod().get().getName();
71 | File appBaseDir = new File(getArtifactGeneBaseDir(), mn);
72 | File appDir = new File(appBaseDir, Apps.GENERATED_SKELETON.dir);
73 | String logsDir = appBaseDir.getAbsolutePath() + File.separator + Apps.GENERATED_SKELETON.dir + "-logs";
74 | String repoDir = getLocalMavenRepoDir();
75 | // The BOM test should run without online registry to verity that it's work
76 | String[] additionalArgs = {"-DquarkusRegistryClient=false"};
77 |
78 | List generatorCmd = getGeneratorCommand(flags, MvnCmds.GENERATOR.mvnCmds[0], extensions, repoDir, additionalArgs);
79 |
80 | List buildCmd = getBuildCommand(MvnCmds.JVM.mvnCmds[0], repoDir);
81 |
82 | List runCmd = getRunCommand(MvnCmds.JVM.mvnCmds[1]);
83 |
84 | URLContent skeletonApp = Apps.GENERATED_SKELETON.urlContent;
85 |
86 | try {
87 | // Cleanup
88 | cleanDirOrFile(appBaseDir.getAbsolutePath());
89 | Files.createDirectories(Paths.get(logsDir));
90 | Files.createDirectories(Paths.get(repoDir));
91 |
92 | //Generator
93 | LOGGER.info("Running inside " + appDir.getAbsolutePath());
94 | LOGGER.info(mn + ": Generator command " + String.join(" ", generatorCmd));
95 | generateLog = new File(logsDir + File.separator + "bom-artifact-generator.log");
96 | ExecutorService buildService = Executors.newFixedThreadPool(1);
97 | buildService.submit(new Commands.ProcessRunner(appBaseDir, generateLog, generatorCmd, 20));
98 | appendln(whatIDidReport, "# " + cn + ", " + mn);
99 | appendln(whatIDidReport, (new Date()).toString());
100 | appendln(whatIDidReport, appBaseDir.getAbsolutePath());
101 | appendlnSection(whatIDidReport, String.join(" ", generatorCmd));
102 | buildService.shutdown();
103 | buildService.awaitTermination(30, TimeUnit.MINUTES);
104 |
105 | assertTrue(generateLog.exists());
106 | checkLog(cn, mn, Apps.GENERATED_SKELETON, MvnCmds.GENERATOR, generateLog);
107 |
108 | // Config, see app-generated-skeleton/README.md
109 | confAppPropsForSkeleton(appDir.getAbsolutePath());
110 | confIndexPageForSkeleton(appDir.getAbsolutePath());
111 | adjustPrettyPrintForJsonLogging(appDir.getAbsolutePath());
112 | dropEntityAnnotations(appDir.getAbsolutePath());
113 | if (StringUtils.isBlank(System.getProperty("gh.actions"))) {
114 | LOGGER.info("Removing repositories and pluginRepositories from pom.xml ...");
115 | removeRepositoriesAndPluginRepositories(appDir + File.separator + "pom.xml");
116 | }
117 |
118 | // Build
119 | LOGGER.info(mn + ": Build command " + String.join(" ", buildCmd));
120 | buildLogA = new File(logsDir + File.separator + "bom-artifact-build.log");
121 | buildService = Executors.newFixedThreadPool(1);
122 | buildService.submit(new Commands.ProcessRunner(appDir, buildLogA, buildCmd, 20));
123 | appendln(whatIDidReport, appDir.getAbsolutePath());
124 | appendlnSection(whatIDidReport, String.join(" ", buildCmd));
125 | buildService.shutdown();
126 | buildService.awaitTermination(30, TimeUnit.MINUTES);
127 |
128 | assertTrue(buildLogA.exists());
129 | checkLog(cn, mn, Apps.GENERATED_SKELETON, MvnCmds.JVM, buildLogA);
130 |
131 | // Run
132 | LOGGER.info(mn + ": Run command " + String.join(" ", MvnCmds.JVM.mvnCmds[1]));
133 | LOGGER.info("Running...");
134 | runLogA = new File(logsDir + File.separator + "bom-artifact-run.log");
135 | appendln(whatIDidReport, appDir.getAbsolutePath());
136 | appendlnSection(whatIDidReport, String.join(" ", runCmd));
137 | pA = runCommand(runCmd, appDir, runLogA);
138 |
139 | // Test web pages
140 | WebpageTester.testWeb(skeletonApp.urlContent[0][0], 20,
141 | skeletonApp.urlContent[0][1], false);
142 |
143 | LOGGER.info("Terminating test and scanning logs...");
144 | pA.getInputStream().available();
145 | checkLog(cn, mn, Apps.GENERATED_SKELETON, MvnCmds.JVM, runLogA);
146 | processStopper(pA, false);
147 | LOGGER.info("Gonna wait for ports closed after run...");
148 | // Release ports
149 | assertTrue(waitForTcpClosed("localhost", parsePort(skeletonApp.urlContent[0][0]), 60),
150 | "Main port is still open after run");
151 |
152 | checkLog(cn, mn, Apps.GENERATED_SKELETON, MvnCmds.JVM, runLogA);
153 |
154 | checkJarSuffixes(flags, appDir);
155 | } finally {
156 | // Make sure processes are down even if there was an exception / failure
157 | if (pA != null) {
158 | processStopper(pA, true);
159 | }
160 | // Archive logs no matter what
161 | archiveLog(cn, mn, generateLog);
162 | if (buildLogA != null) {
163 | archiveLog(cn, mn, buildLogA);
164 | }
165 | if (runLogA != null) {
166 | archiveLog(cn, mn, runLogA);
167 | }
168 | writeReport(cn, mn, whatIDidReport.toString());
169 | cleanDirOrFile(appBaseDir.getAbsolutePath());
170 | }
171 | }
172 |
173 | @Test
174 | @Tag("product-and-community")
175 | public void quarkusBomExtensionsA(TestInfo testInfo) throws Exception {
176 | testRuntime(testInfo, supportedExtensionsSubsetSetA, EnumSet.of(TestFlags.QUARKUS_BOM));
177 | }
178 |
179 | @Test
180 | @Tag("product-and-community")
181 | public void quarkusBomExtensionsB(TestInfo testInfo) throws Exception {
182 | testRuntime(testInfo, supportedExtensionsSubsetSetB, EnumSet.of(TestFlags.QUARKUS_BOM));
183 | }
184 |
185 | @Test
186 | @Tag("product")
187 | public void quarkusProductBomExtensionsA(TestInfo testInfo) throws Exception {
188 | testRuntime(testInfo, supportedExtensionsSubsetSetA, EnumSet.of(TestFlags.PRODUCT_BOM));
189 | }
190 |
191 | @Test
192 | @Tag("product")
193 | public void quarkusProductBomExtensionsB(TestInfo testInfo) throws Exception {
194 | testRuntime(testInfo, supportedExtensionsSubsetSetB, EnumSet.of(TestFlags.PRODUCT_BOM));
195 | }
196 |
197 | @Test
198 | @Tag("product-and-community")
199 | public void quarkusBomReactiveExtensionsA(TestInfo testInfo) throws Exception {
200 | testRuntime(testInfo, supportedReactiveExtensionsSubsetSetA, EnumSet.of(TestFlags.QUARKUS_BOM));
201 | }
202 |
203 | @Test
204 | @Tag("product-and-community")
205 | public void quarkusBomReactiveExtensionsB(TestInfo testInfo) throws Exception {
206 | testRuntime(testInfo, supportedReactiveExtensionsSubsetSetB, EnumSet.of(TestFlags.QUARKUS_BOM));
207 | }
208 |
209 | @Test
210 | @Tag("product")
211 | public void quarkusProductBomReactiveExtensionsA(TestInfo testInfo) throws Exception {
212 | testRuntime(testInfo, supportedReactiveExtensionsSubsetSetA, EnumSet.of(TestFlags.PRODUCT_BOM));
213 | }
214 |
215 | @Test
216 | @Tag("product")
217 | public void quarkusProductBomReactiveExtensionsB(TestInfo testInfo) throws Exception {
218 | testRuntime(testInfo, supportedReactiveExtensionsSubsetSetB, EnumSet.of(TestFlags.PRODUCT_BOM));
219 | }
220 |
221 | @Test
222 | @Tag("product-and-community")
223 | public void langchainExtensions(TestInfo testInfo) throws Exception {
224 | testRuntime(testInfo, langchain4jExtensions, EnumSet.of(TestFlags.QUARKUS_BOM));
225 | }
226 |
227 | @Test
228 | @Tag("product")
229 | public void productLangchainExtensions(TestInfo testInfo) throws Exception {
230 | testRuntime(testInfo, langchain4jExtensions, EnumSet.of(TestFlags.PRODUCT_BOM));
231 | }
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/SpecialCharsTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.nio.charset.StandardCharsets;
6 | import java.nio.file.Files;
7 | import java.nio.file.Path;
8 | import java.util.ArrayList;
9 | import java.util.Arrays;
10 | import java.util.Date;
11 | import java.util.List;
12 | import java.util.concurrent.ExecutorService;
13 | import java.util.concurrent.Executors;
14 | import java.util.concurrent.TimeUnit;
15 |
16 | import io.quarkus.ts.startstop.utils.Apps;
17 | import io.quarkus.ts.startstop.utils.Commands;
18 | import io.quarkus.ts.startstop.utils.MvnCmds;
19 | import io.quarkus.ts.startstop.utils.WebpageTester;
20 | import org.apache.commons.io.FileUtils;
21 | import org.jboss.logging.Logger;
22 | import org.junit.jupiter.api.Disabled;
23 | import org.junit.jupiter.api.Tag;
24 | import org.junit.jupiter.api.Test;
25 | import org.junit.jupiter.api.TestInfo;
26 | import org.junit.jupiter.api.condition.DisabledOnOs;
27 | import org.junit.jupiter.api.condition.OS;
28 |
29 | import static io.quarkus.ts.startstop.utils.Commands.cleanTarget;
30 | import static io.quarkus.ts.startstop.utils.Commands.getBaseDir;
31 | import static io.quarkus.ts.startstop.utils.Commands.getBuildCommand;
32 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusGroupId;
33 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusVersion;
34 | import static io.quarkus.ts.startstop.utils.Commands.getRunCommand;
35 | import static io.quarkus.ts.startstop.utils.Commands.processStopper;
36 | import static io.quarkus.ts.startstop.utils.Commands.runCommand;
37 | import static io.quarkus.ts.startstop.utils.Logs.appendln;
38 | import static io.quarkus.ts.startstop.utils.Logs.appendlnSection;
39 | import static io.quarkus.ts.startstop.utils.Logs.archiveLog;
40 | import static io.quarkus.ts.startstop.utils.Logs.checkLog;
41 | import static io.quarkus.ts.startstop.utils.Logs.writeReport;
42 | import static org.junit.jupiter.api.Assertions.assertTrue;
43 |
44 | /**
45 | * Tests for running Quarkus applications on path with special characters
46 | */
47 | @Tag("special-chars")
48 | public class SpecialCharsTest {
49 | private static final Logger LOGGER = Logger.getLogger(SpecialCharsTest.class.getName());
50 |
51 | public static final String BASE_DIR = getBaseDir();
52 |
53 | public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, String subdir) throws IOException, InterruptedException {
54 | Process pA = null;
55 | File buildLogA = null;
56 | File runLogA = null;
57 | StringBuilder whatIDidReport = new StringBuilder();
58 | File appBaseDir = new File(BASE_DIR, app.dir);
59 | File appDestDir = new File(appBaseDir.getParentFile(), subdir);
60 | File appDir = new File(appDestDir, app.dir);
61 | File appPomXml = new File(appDir, "pom.xml");
62 | File logsDir = new File(appDir, "special-chars-logs");
63 | String canonicalName = testInfo.getTestClass().get().getCanonicalName();
64 | String methodName = testInfo.getTestMethod().get().getName();
65 | LOGGER.info("Testing app: " + app + ", mode: " + mvnCmds.toString() + ", on path " + appDestDir);
66 | try {
67 | // Clean target directory
68 | cleanTarget(app);
69 |
70 | removeDirWithSpecialCharacters(appDestDir);
71 |
72 | // Make dir with special chars
73 | if (!appDestDir.mkdir()) {
74 | throw new RuntimeException("Cannot create directory " + appDestDir);
75 | }
76 |
77 | // Copy to path with special characters
78 | LOGGER.info("Copying " + appBaseDir + "to" + appDestDir);
79 | FileUtils.copyDirectoryToDirectory(appBaseDir, appDestDir);
80 |
81 | // Replace relative path to parent project
82 | Path path = appPomXml.toPath();
83 | String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
84 | content = content
85 | .replaceAll("..", "../..")
86 | .replaceAll("app-jakarta-rest-minimal", "app-jakarta-rest-minimal-copy");
87 | Files.write(path, content.getBytes(StandardCharsets.UTF_8));
88 |
89 | // Create logs directory
90 | Files.createDirectories(logsDir.toPath());
91 |
92 | List cmd;
93 | // Build
94 | if (mvnCmds != MvnCmds.DEV) {
95 | buildLogA = new File(logsDir + File.separator + subdir + "-" + mvnCmds.name().toLowerCase() + "-build.log");
96 | ExecutorService buildService = Executors.newFixedThreadPool(1);
97 | List baseBuildCmd = new ArrayList<>();
98 | baseBuildCmd.addAll(Arrays.asList(mvnCmds.mvnCmds[0]));
99 | baseBuildCmd.add("-Dquarkus.version=" + getQuarkusVersion());
100 | baseBuildCmd.add("-Dquarkus.platform.group-id=" + getQuarkusGroupId());
101 | cmd = getBuildCommand(baseBuildCmd.toArray(new String[0]));
102 |
103 | appendln(whatIDidReport, "# " + canonicalName + ", " + methodName);
104 | appendln(whatIDidReport, (new Date()).toString());
105 | appendln(whatIDidReport, appDir.getAbsolutePath());
106 | appendlnSection(whatIDidReport, String.join(" ", cmd));
107 |
108 | LOGGER.info("Building (" + cmd + ")");
109 | buildService.submit(new Commands.ProcessRunner(appDir, buildLogA, cmd, 20));
110 |
111 | buildService.shutdown();
112 | buildService.awaitTermination(30, TimeUnit.MINUTES);
113 |
114 | assertTrue(buildLogA.exists());
115 | checkLog(canonicalName, methodName, app, mvnCmds, buildLogA);
116 | }
117 |
118 | // Run
119 | runLogA = new File(logsDir + File.separator + subdir + "-" + mvnCmds.name().toLowerCase() + "-run.log");
120 |
121 | if (mvnCmds == MvnCmds.DEV) {
122 | List baseBuildCmd = new ArrayList<>();
123 | baseBuildCmd.addAll(Arrays.asList(mvnCmds.mvnCmds[0]));
124 | baseBuildCmd.add("-Dquarkus.version=" + getQuarkusVersion());
125 | baseBuildCmd.add("-Dquarkus.platform.group-id=" + getQuarkusGroupId());
126 | cmd = getRunCommand(baseBuildCmd.toArray(new String[0]));
127 | } else {
128 | cmd = getRunCommand(mvnCmds.mvnCmds[1]);
129 | }
130 | LOGGER.info("Running (" + cmd + ")");
131 | appendln(whatIDidReport, appDir.getAbsolutePath());
132 | appendlnSection(whatIDidReport, String.join(" ", cmd));
133 | pA = runCommand(cmd, appDir, runLogA);
134 |
135 | // Test web page
136 | LOGGER.info("Testing web page content...");
137 | int timeout = mvnCmds != MvnCmds.DEV ? 30 : 120;
138 | for (String[] urlContent : app.urlContent.urlContent) {
139 | WebpageTester.testWeb(urlContent[0], timeout, urlContent[1], false);
140 | }
141 |
142 | processStopper(pA, false);
143 | } finally {
144 | // Make sure processes are down even if there was an exception / failure
145 | if (pA != null) {
146 | processStopper(pA, true);
147 | pA.waitFor();
148 | }
149 |
150 | // Archive logs
151 | if (buildLogA != null) {
152 | archiveLog(canonicalName, methodName, buildLogA);
153 | }
154 | if (runLogA != null) {
155 | archiveLog(canonicalName, methodName, runLogA);
156 | }
157 | writeReport(canonicalName, methodName, whatIDidReport.toString());
158 |
159 | removeDirWithSpecialCharacters(appDestDir);
160 | }
161 | }
162 |
163 | @Test
164 | public void spacesJVM(TestInfo testInfo) throws IOException, InterruptedException {
165 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.JVM, "s p a c e s j v m");
166 | }
167 |
168 | @Test
169 | public void spacesDEV(TestInfo testInfo) throws IOException, InterruptedException {
170 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.DEV, "s p a c e s d e v");
171 | }
172 |
173 | @Test
174 | @Tag("native")
175 | public void spacesNative(TestInfo testInfo) throws IOException, InterruptedException {
176 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.NATIVE, "s p a c e s n a t i v e");
177 | }
178 |
179 | @Test
180 | public void specialJVM(TestInfo testInfo) throws IOException, InterruptedException {
181 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.JVM, ",;~!@#$%^&()");
182 | }
183 |
184 | @Test
185 | @DisabledOnOs(value = {OS.WINDOWS}, disabledReason = "https://github.com/quarkusio/quarkus/issues/42942")
186 | public void specialDEV(TestInfo testInfo) throws IOException, InterruptedException {
187 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.DEV, ",;~!@#$%^&()");
188 | }
189 |
190 | @Test
191 | @Tag("native")
192 | @DisabledOnOs(value = {OS.WINDOWS}, disabledReason = "Native on Windows is not supported")
193 | public void specialNative(TestInfo testInfo) throws IOException, InterruptedException {
194 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.NATIVE, ",;~!@#$%^&()");
195 | }
196 |
197 | @Test
198 | public void diacriticsJVM(TestInfo testInfo) throws IOException, InterruptedException {
199 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.JVM, "ěščřžýáíéůú");
200 | }
201 |
202 | @Test
203 | @DisabledOnOs(value = {OS.WINDOWS},disabledReason = "Needs env setup, https://github.com/quarkusio/quarkus/issues/9707 is fixed, tests pass on local Windows 10 with Czech language as default")
204 | public void diacriticsDEV(TestInfo testInfo) throws IOException, InterruptedException {
205 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.DEV, "ěščřžýáíéůú");
206 | }
207 |
208 | @Test
209 | @Tag("native")
210 | @DisabledOnOs(value = {OS.WINDOWS}, disabledReason = "Native on Windows is not supported")
211 | public void diacriticsNative(TestInfo testInfo) throws IOException, InterruptedException {
212 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.NATIVE, "ěščřžýáíéůú");
213 | }
214 |
215 | @Test
216 | public void japaneseJVM(TestInfo testInfo) throws IOException, InterruptedException {
217 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.JVM, "元気かい");
218 | }
219 |
220 | @Test
221 | @DisabledOnOs(value = {OS.WINDOWS},disabledReason = "Needs env setup, https://github.com/quarkusio/quarkus/issues/9707 is fixed, tests pass on local Windows 10 with Czech language as default")
222 | public void japaneseDEV(TestInfo testInfo) throws IOException, InterruptedException {
223 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.DEV, "元気かい");
224 | }
225 |
226 | @Test
227 | @Tag("native")
228 | @DisabledOnOs(value = {OS.WINDOWS}, disabledReason = "Native on Windows is not supported")
229 | public void japaneseNative(TestInfo testInfo) throws IOException, InterruptedException {
230 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.NATIVE, "元気かい");
231 | }
232 |
233 | @Test
234 | public void otherJVM(TestInfo testInfo) throws IOException, InterruptedException {
235 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.JVM, "Îñţérñåţîöñåļîžåţîờñ");
236 | }
237 |
238 | @Test
239 | @DisabledOnOs(value = {OS.WINDOWS},disabledReason = "Needs env setup, https://github.com/quarkusio/quarkus/issues/9707 is fixed, tests pass on local Windows 10 with Czech language as default")
240 | public void otherDEV(TestInfo testInfo) throws IOException, InterruptedException {
241 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.DEV, "Îñţérñåţîöñåļîžåţîờñ");
242 | }
243 |
244 | @Test
245 | @Tag("native")
246 | @DisabledOnOs(value = {OS.WINDOWS}, disabledReason = "Native on Windows is not supported")
247 | public void otherNative(TestInfo testInfo) throws IOException, InterruptedException {
248 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.NATIVE, "Îñţérñåţîöñåļîžåţîờñ");
249 | }
250 |
251 | private void removeDirWithSpecialCharacters(File appDestDir) {
252 | // Remove dir with special chars
253 | try {
254 | if (appDestDir.exists()) {
255 | FileUtils.deleteDirectory(appDestDir);
256 | }
257 | } catch (IOException ignored) {
258 | // ignored when the folder could not be deleted.
259 | }
260 | }
261 |
262 | }
263 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/redhat/CodeQuarkusRedHatSiteTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.redhat;
2 |
3 | import com.microsoft.playwright.Browser;
4 | import com.microsoft.playwright.BrowserContext;
5 | import com.microsoft.playwright.BrowserType;
6 | import com.microsoft.playwright.Locator;
7 | import com.microsoft.playwright.Page;
8 | import com.microsoft.playwright.Playwright;
9 | import com.microsoft.playwright.options.ElementState;
10 | import io.quarkus.ts.startstop.utils.Commands;
11 | import org.jboss.logging.Logger;
12 | import org.junit.jupiter.api.AfterAll;
13 | import org.junit.jupiter.api.Assumptions;
14 | import org.junit.jupiter.api.BeforeAll;
15 | import org.junit.jupiter.api.Tag;
16 | import org.junit.jupiter.api.TestInfo;
17 | import org.junit.jupiter.api.Test;
18 | import org.junit.jupiter.api.TestInstance;
19 |
20 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusVersion;
21 | import static org.junit.jupiter.api.Assertions.assertEquals;
22 | import static org.junit.jupiter.api.Assertions.assertFalse;
23 | import static org.junit.jupiter.api.Assertions.assertNotNull;
24 | import static org.junit.jupiter.api.Assertions.assertTrue;
25 |
26 | /**
27 | * Tests for checking presence of element on webpage
28 | */
29 | @Tag("codequarkus")
30 | @TestInstance(TestInstance.Lifecycle.PER_CLASS)
31 | public class CodeQuarkusRedHatSiteTest {
32 |
33 | private static final Logger LOGGER = Logger.getLogger(CodeQuarkusRedHatSiteTest.class.getName());
34 |
35 | public static final String pageLoadedSelector = ".project-extensions";
36 | public static final String pageWithExtensionLoadedSelector = ".extension-picker-list";
37 | public static final String webPageUrl = Commands.getCodeQuarkusURL("https://code.quarkus.redhat.com/");
38 | public static final String elementTitleByText = "Quarkus - Start coding with code.quarkus.redhat.com";
39 | public static final String elementIconByXpath = "//link[@rel=\"shortcut icon\"][@href=\"https://www.redhat.com/favicon.ico\"]";
40 | public static final String elementJavaVersionSelectByXpath = "//select[@id=\"javaversion\"]";
41 | public static final String elementRedHatLogoByXpath = "//img[@class=\"logo\"][@alt=\"Red Hat Logo\"]";
42 | public static final String elementStreamPickerByXpath = "//div[@class=\"stream-picker dropdown\"]";
43 | public static final String elementStreamItemsByXpath = "//div[@class=\"dropdown-item\"]";
44 | public static final String elementSupportedFlagByXpath = "//div[@class=\"extension-tag support-full-support dropdown-toggle\"]";
45 | public static final String elementQuarkusPlatformVersionByXpath = "//div[contains(@class, 'quarkus-stream')]";
46 | public static final String elementExtensionByXpath = "//div[@class=\"extension-row\" and @aria-label=\"%s\"]";
47 | public static final String elementSupportFilterXpath = "//div[@class='filter-combo-button dropdown-toggle' and @aria-label=\"Toggle support combobox\"]";
48 | public static final String elementIntroductionModalWindowXpath = "//button[@aria-label=\"Close the introduction modal\"]";
49 | public static final String elementExpandExtensionXpath = "//button[@class=\"button-show-more btn-light btn btn-primary\"]";
50 |
51 | public static final String QUARKUS_REST_EXTENSION = "io.quarkus:quarkus-rest";
52 | public static final String QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION = "io.quarkiverse.langchain4j:quarkus-langchain4j-openai";
53 | public static final String QUARKUS_REST_LINKS_EXTENSION = "io.quarkus:quarkus-rest-links";
54 |
55 | private BrowserContext browserContext; // Operates in incognito mode
56 |
57 | @BeforeAll
58 | public void init(){
59 | Playwright playwright = Playwright.create();
60 | Browser browser = playwright.firefox().launch(new BrowserType.LaunchOptions().setHeadless(true));
61 | Browser.NewContextOptions options = new Browser.NewContextOptions();
62 | options.ignoreHTTPSErrors = true;
63 | browserContext = browser.newContext(options);
64 | LOGGER.info("Incognito browser session has been created");
65 | }
66 |
67 | @AfterAll
68 | public void close(){
69 | browserContext.close();
70 | }
71 |
72 |
73 | @Test
74 | public void validatePresenceOfIcon(TestInfo testInfo) {
75 | Page page = loadPage(webPageUrl, 60);
76 | LOGGER.info("Trying to find element: " + elementIconByXpath);
77 | Locator icon = page.locator(elementIconByXpath);
78 | assertEquals(1, icon.count(), "Element: " + elementIconByXpath + " is missing!");
79 | }
80 |
81 | @Test
82 | public void validatePresenceOfTitle(TestInfo testInfo) {
83 | Page page = loadPage(webPageUrl, 60);
84 | LOGGER.info("Verify page title is: " + elementTitleByText);
85 | assertEquals(elementTitleByText, page.title(),
86 | "Title doesn't match. Found on the web: " + page.title() + ". Expected: " + elementTitleByText);
87 | }
88 |
89 | @Test
90 | public void validatePresenceOfRedHatLogo(TestInfo testInfo) {
91 | Page page = loadPage(webPageUrl, 60);
92 | LOGGER.info("Trying to find element: " + elementRedHatLogoByXpath);
93 | Locator redHatLogo = page.locator(elementRedHatLogoByXpath);
94 | redHatLogo.elementHandle().waitForElementState(ElementState.VISIBLE);
95 | assertTrue(redHatLogo.isVisible(), "Element: " + elementRedHatLogoByXpath + " is missing or not visible!");
96 | }
97 |
98 | @Test
99 | public void validatePresenceOfSupportedFlags(TestInfo testInfo) {
100 | Page page = loadPage(webPageUrl + "/?e=grpc", 60);
101 | LOGGER.info("Trying to find element: " + elementSupportedFlagByXpath);
102 | Locator supportedExtensions = page.locator(elementSupportedFlagByXpath);
103 | assertTrue(supportedExtensions.count() >= 1, "Element: " + elementSupportedFlagByXpath + " is missing!");
104 | }
105 |
106 | @Test
107 | public void validatePresenceOfJavaVersionSelect(TestInfo testInfo) {
108 | Page page = loadPage(webPageUrl, 60);
109 | LOGGER.info("Trying to find element: " + elementJavaVersionSelectByXpath);
110 | Locator javaVersionSelect = page.locator(elementJavaVersionSelectByXpath);
111 | assertTrue(javaVersionSelect.count() == 1, "Element: " + elementJavaVersionSelectByXpath + " is missing!");
112 |
113 | String javaVersionText = javaVersionSelect.textContent();
114 | assertTrue(javaVersionText.contains("21"), "Java 21 is missing in java version select! javaVersionText: " + javaVersionText);
115 | assertTrue(javaVersionText.contains("17"), "Java 17 is missing in java version select! javaVersionText: " + javaVersionText);
116 | }
117 |
118 | @Test
119 | public void validatePresenceOfStreamVersionSelect(TestInfo testInfo) {
120 | Page page = loadPage(webPageUrl, 60);
121 | LOGGER.info("Trying to find element: " + elementStreamPickerByXpath);
122 | Locator streamPicker = page.locator(elementStreamPickerByXpath);
123 | assertTrue(streamPicker.isVisible(), "Element: " + streamPicker + " is missing!");
124 |
125 | LOGGER.info("Trying to find elements: " + elementStreamItemsByXpath);
126 | streamPicker.click();
127 | Locator streamItems = page.locator(elementStreamItemsByXpath);
128 | assertTrue(streamItems.count() > 0, "No stream is defined");
129 | assertTrue(streamItems.count() > 1, "Two (or more) streams are expected to be defined defined, streamItems count: " + streamItems.count() + "\n" +
130 | "Product Update and Support Policy: https://access.redhat.com/support/policy/updates/jboss_notes#p_quarkus");
131 | }
132 |
133 | @Test
134 | public void validateQuarkusVersionMatch(TestInfo testInfo) {
135 | String quarkusPlatformVersion = getQuarkusVersion();
136 | Assumptions.assumeTrue(quarkusPlatformVersion.contains("redhat"));
137 |
138 | Page page = loadPage(webPageUrl, 60);
139 | LOGGER.info("Trying to find element: " + elementQuarkusPlatformVersionByXpath);
140 | String quarkusPlatformVersionFromWeb = page.locator(elementQuarkusPlatformVersionByXpath).elementHandle().getAttribute("title");
141 |
142 | assertNotNull(quarkusPlatformVersionFromWeb, "Element: " + elementQuarkusPlatformVersionByXpath + " is missing!");
143 | assertTrue(quarkusPlatformVersionFromWeb.contains(quarkusPlatformVersion),
144 | "Quarkus versions doesn't match. Found on the web: " + quarkusPlatformVersionFromWeb + ". Expected: " + quarkusPlatformVersion);
145 | }
146 |
147 | @Test
148 | public void validateQuarkusSearchForAllSupportedExtensions(TestInfo testInfo) {
149 | Page page = loadPageWithExtensions(webPageUrl + "?extension-search=support:*", 60);
150 |
151 | showAllExtension(page);
152 |
153 | // Check if the supported extension are visible
154 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION));
155 | assertTrue(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION)).isVisible(),
156 | "The extension " + QUARKUS_REST_EXTENSION + " should be visible as it's supported.");
157 |
158 | // Check that the unsupported extension is not visible
159 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION));
160 | assertFalse(page.locator(elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION)).isVisible(),
161 | "The extension " + QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION + " should not be visible as it isn't supported.");
162 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION));
163 | assertFalse(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION)).isVisible(),
164 | "The extension " + QUARKUS_REST_LINKS_EXTENSION + " should not be visible as it isn't supported.");
165 | }
166 |
167 | @Test
168 | public void validateQuarkusSearchForAllNotSupportedExtensions(TestInfo testInfo) {
169 | Page page = loadPageWithExtensions(webPageUrl + "?extension-search=!support", 60);
170 |
171 | showAllExtension(page);
172 | // Check if the supported extension are not visible
173 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION));
174 | assertFalse(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION)).isVisible(),
175 | "The extension " + QUARKUS_REST_EXTENSION + " should not be visible as it's supported.");
176 |
177 | // Check that the unsupported extension is visible
178 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION));
179 | assertTrue(page.locator(elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION)).isVisible(),
180 | "The extension " + QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION + " should be visible as it isn't supported.");
181 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION));
182 | assertTrue(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION)).isVisible(),
183 | "The extension " + QUARKUS_REST_LINKS_EXTENSION + " should be visible as it isn't supported.");
184 | }
185 |
186 | @Test
187 | public void checkAllSupportOptionArePresent(TestInfo testInfo) {
188 | String supportButtonXpath = "//button[@aria-label=\"%s\"]";
189 | String supportDivXpath = "//div[@aria-label=\"Add support:%s filter\"]";
190 | // TODO add `dev-preview` when the code.quarkus is updated
191 | String[] supportScopes = {"full-support", "tech-preview", "dev-support", "supported-in-jvm", "deprecated"};
192 |
193 | Page page = loadPage(webPageUrl, 60);
194 | // The support menu is not visible without clicking at it
195 | closeIntroductionModalWindow(page);
196 | page.locator(elementSupportFilterXpath).click();
197 |
198 | assertTrue(page.locator(supportButtonXpath.formatted("Add has support filter")).isVisible(),
199 | "The option to show all supported extensions should be present under the support filter.");
200 | assertTrue(page.locator(supportButtonXpath.formatted("Add no support filter")).isVisible(),
201 | "The option to show all unsupported extensions should be present under the support filter.");
202 |
203 | for (String supportScope : supportScopes) {
204 | assertTrue(page.locator(supportDivXpath.formatted(supportScope)).isVisible(),
205 | "The support scope " + supportScope + "should be present under the support filter.");
206 | }
207 | }
208 |
209 | public void closeIntroductionModalWindow(Page page) {
210 | page.locator(elementIntroductionModalWindowXpath).click();
211 | }
212 |
213 | public void showAllExtension(Page page){
214 | // It's needed to show all extension, otherwise all filtered list of extension is not visible
215 | closeIntroductionModalWindow(page);
216 | page.locator(elementExpandExtensionXpath).click();
217 | }
218 |
219 | public Page loadPage(String url, int timeoutSeconds) {
220 | return loadPageSettingWaitSelector(url, timeoutSeconds, pageLoadedSelector);
221 | }
222 |
223 | public Page loadPageWithExtensions(String url, int timeoutSeconds) {
224 | return loadPageSettingWaitSelector(url, timeoutSeconds, pageWithExtensionLoadedSelector);
225 | }
226 |
227 | public Page loadPageSettingWaitSelector(String url, int timeoutSeconds, String selector) {
228 | LOGGER.info("Loading web page " + url);
229 | Page page = browserContext.newPage(); // this will create new tab inside browser
230 | page.setDefaultTimeout(timeoutSeconds * 1000);
231 | page.navigate(url);
232 | page.waitForSelector(selector);
233 | return page;
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/ibm/CodeQuarkusIbmSiteTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop.ibm;
2 |
3 | import com.microsoft.playwright.Browser;
4 | import com.microsoft.playwright.BrowserContext;
5 | import com.microsoft.playwright.BrowserType;
6 | import com.microsoft.playwright.Locator;
7 | import com.microsoft.playwright.Page;
8 | import com.microsoft.playwright.Playwright;
9 | import com.microsoft.playwright.options.ElementState;
10 | import io.quarkus.ts.startstop.utils.Commands;
11 | import org.jboss.logging.Logger;
12 | import org.junit.jupiter.api.AfterAll;
13 | import org.junit.jupiter.api.Assumptions;
14 | import org.junit.jupiter.api.BeforeAll;
15 | import org.junit.jupiter.api.Tag;
16 | import org.junit.jupiter.api.Test;
17 | import org.junit.jupiter.api.TestInfo;
18 | import org.junit.jupiter.api.TestInstance;
19 |
20 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusVersion;
21 | import static org.junit.jupiter.api.Assertions.assertEquals;
22 | import static org.junit.jupiter.api.Assertions.assertFalse;
23 | import static org.junit.jupiter.api.Assertions.assertNotNull;
24 | import static org.junit.jupiter.api.Assertions.assertTrue;
25 |
26 | /**
27 | * Tests for checking presence of element on webpage
28 | */
29 | @Tag("codequarkus")
30 | @TestInstance(TestInstance.Lifecycle.PER_CLASS)
31 | public class CodeQuarkusIbmSiteTest {
32 |
33 | private static final Logger LOGGER = Logger.getLogger(CodeQuarkusIbmSiteTest.class.getName());
34 |
35 | public static final String pageLoadedSelector = ".project-extensions";
36 | public static final String pageWithExtensionLoadedSelector = ".extension-picker-list";
37 | public static final String webPageUrl = Commands.getCodeQuarkusURL("https://code.quarkus.ibm.com/");
38 | public static final String elementTitleByText = "Quarkus - Start coding with code.quarkus.ibm.com";
39 | public static final String elementIconByXpath = "//link[@rel=\"shortcut icon\"][@href=\"https://www.ibm.com/content/dam/adobe-cms/default-images/icon-32x32.png\"]";
40 | public static final String elementJavaVersionSelectByXpath = "//select[@id=\"javaversion\"]";
41 | public static final String elementBrandNameByXpath = "//div[@class=\"brand\" and contains(text(), 'Enterprise Build of Quarkus')]";
42 | public static final String elementStreamPickerByXpath = "//div[@class=\"stream-picker dropdown\"]";
43 | public static final String elementStreamItemsByXpath = "//div[@class=\"dropdown-item\"]";
44 | public static final String elementSupportedFlagByXpath = "//div[@class=\"extension-tag support-full-support dropdown-toggle\"]";
45 | public static final String elementQuarkusPlatformVersionByXpath = "//div[contains(@class, 'quarkus-stream')]";
46 | public static final String elementExtensionByXpath = "//div[@class=\"extension-row\" and @aria-label=\"%s\"]";
47 | public static final String elementSupportFilterXpath = "//div[@class='filter-combo-button dropdown-toggle' and @aria-label=\"Toggle support combobox\"]";
48 | public static final String elementIntroductionModalWindowXpath = "//button[@aria-label=\"Close the introduction modal\"]";
49 | public static final String elementExpandExtensionXpath = "//button[@class=\"button-show-more btn-light btn btn-primary\"]";
50 |
51 | public static final String QUARKUS_REST_EXTENSION = "io.quarkus:quarkus-rest";
52 | public static final String QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION = "io.quarkiverse.langchain4j:quarkus-langchain4j-openai";
53 | public static final String QUARKUS_REST_LINKS_EXTENSION = "io.quarkus:quarkus-rest-links";
54 |
55 | private BrowserContext browserContext; // Operates in incognito mode
56 |
57 | @BeforeAll
58 | public void init(){
59 | Playwright playwright = Playwright.create();
60 | Browser browser = playwright.firefox().launch(new BrowserType.LaunchOptions().setHeadless(true));
61 | Browser.NewContextOptions options = new Browser.NewContextOptions();
62 | options.ignoreHTTPSErrors = true;
63 | browserContext = browser.newContext(options);
64 | LOGGER.info("Incognito browser session has been created");
65 | }
66 |
67 | @AfterAll
68 | public void close(){
69 | browserContext.close();
70 | }
71 |
72 |
73 | @Test
74 | public void validatePresenceOfIcon(TestInfo testInfo) {
75 | Page page = loadPage(webPageUrl, 60);
76 | LOGGER.info("Trying to find element: " + elementIconByXpath);
77 | Locator icon = page.locator(elementIconByXpath);
78 | assertEquals(1, icon.count(), "Element: " + elementIconByXpath + " is missing!");
79 | }
80 |
81 | @Test
82 | public void validatePresenceOfTitle(TestInfo testInfo) {
83 | Page page = loadPage(webPageUrl, 60);
84 | LOGGER.info("Verify page title is: " + elementTitleByText);
85 | assertEquals(elementTitleByText, page.title(),
86 | "Title doesn't match. Found on the web: " + page.title() + ". Expected: " + elementTitleByText);
87 | }
88 |
89 | @Test
90 | public void validatePresenceOfBrandName(TestInfo testInfo) {
91 | Page page = loadPage(webPageUrl, 60);
92 | LOGGER.info("Trying to find element: " + elementBrandNameByXpath);
93 | Locator brandName = page.locator(elementBrandNameByXpath);
94 | brandName.elementHandle().waitForElementState(ElementState.VISIBLE);
95 | assertTrue(brandName.isVisible(), "Element: " + elementBrandNameByXpath + " is missing or not visible!");
96 | }
97 |
98 | @Test
99 | public void validatePresenceOfSupportedFlags(TestInfo testInfo) {
100 | Page page = loadPage(webPageUrl + "/?e=grpc", 60);
101 | LOGGER.info("Trying to find element: " + elementSupportedFlagByXpath);
102 | Locator supportedExtensions = page.locator(elementSupportedFlagByXpath);
103 | assertTrue(supportedExtensions.count() >= 1, "Element: " + elementSupportedFlagByXpath + " is missing!");
104 | }
105 |
106 | @Test
107 | public void validatePresenceOfJavaVersionSelect(TestInfo testInfo) {
108 | Page page = loadPage(webPageUrl, 60);
109 | LOGGER.info("Trying to find element: " + elementJavaVersionSelectByXpath);
110 | Locator javaVersionSelect = page.locator(elementJavaVersionSelectByXpath);
111 | assertTrue(javaVersionSelect.count() == 1, "Element: " + elementJavaVersionSelectByXpath + " is missing!");
112 |
113 | String javaVersionText = javaVersionSelect.textContent();
114 | assertTrue(javaVersionText.contains("21"), "Java 21 is missing in java version select! javaVersionText: " + javaVersionText);
115 | assertTrue(javaVersionText.contains("17"), "Java 17 is missing in java version select! javaVersionText: " + javaVersionText);
116 | }
117 |
118 | @Test
119 | public void validatePresenceOfStreamVersionSelect(TestInfo testInfo) {
120 | Page page = loadPage(webPageUrl, 60);
121 | LOGGER.info("Trying to find element: " + elementStreamPickerByXpath);
122 | Locator streamPicker = page.locator(elementStreamPickerByXpath);
123 | assertTrue(streamPicker.isVisible(), "Element: " + streamPicker + " is missing!");
124 |
125 | LOGGER.info("Trying to find elements: " + elementStreamItemsByXpath);
126 | streamPicker.click();
127 | Locator streamItems = page.locator(elementStreamItemsByXpath);
128 | assertTrue(streamItems.count() > 0, "No stream is defined");
129 | /*
130 | TODO enable this when there is more then 1 stream. Atm only 3.27 will be available
131 | if (!webPageUrl.contains("apps.ocp-c1")) { // build-scoped instances have just the current stream defined
132 | assertTrue(streamItems.count() > 1, "Two (or more) streams are expected to be defined defined, streamItems count: " + streamItems.count() + "\n" +
133 | "Product Update and Support Policy: https://access.redhat.com/support/policy/updates/jboss_notes#p_quarkus");
134 | }
135 | */
136 | }
137 |
138 | @Test
139 | public void validateQuarkusVersionMatch(TestInfo testInfo) {
140 | String quarkusPlatformVersion = getQuarkusVersion();
141 | // TODO change this to IBM when it have standalone platform name
142 | Assumptions.assumeTrue(quarkusPlatformVersion.contains("redhat"));
143 |
144 | Page page = loadPage(webPageUrl, 60);
145 | LOGGER.info("Trying to find element: " + elementQuarkusPlatformVersionByXpath);
146 | String quarkusPlatformVersionFromWeb = page.locator(elementQuarkusPlatformVersionByXpath).elementHandle().getAttribute("title");
147 |
148 | assertNotNull(quarkusPlatformVersionFromWeb, "Element: " + elementQuarkusPlatformVersionByXpath + " is missing!");
149 | assertTrue(quarkusPlatformVersionFromWeb.contains(quarkusPlatformVersion),
150 | "Quarkus versions doesn't match. Found on the web: " + quarkusPlatformVersionFromWeb + ". Expected: " + quarkusPlatformVersion);
151 | }
152 |
153 | @Test
154 | public void validateQuarkusSearchForAllSupportedExtensions(TestInfo testInfo) {
155 | Page page = loadPageWithExtensions(webPageUrl + "?extension-search=support:*", 60);
156 | showAllExtension(page);
157 | // Check if the supported extension are visible
158 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION));
159 | assertTrue(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION)).isVisible(),
160 | "The extension " + QUARKUS_REST_EXTENSION + " should be visible as it's supported.");
161 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION));
162 | assertTrue(page.locator(elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION)).isVisible(),
163 | "The extension " + QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION + " should be visible as it's supported.");
164 |
165 | // Check that the unsupported extension is not visible
166 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION));
167 | assertFalse(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION)).isVisible(),
168 | "The extension " + QUARKUS_REST_LINKS_EXTENSION + " should not be visible as it isn't supported.");
169 | }
170 |
171 | @Test
172 | public void validateQuarkusSearchForAllNotSupportedExtensions(TestInfo testInfo) {
173 | Page page = loadPageWithExtensions(webPageUrl + "?extension-search=!support", 60);
174 | showAllExtension(page);
175 | // Check if the supported extension are not visible
176 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION));
177 | assertFalse(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_EXTENSION)).isVisible(),
178 | "The extension " + QUARKUS_REST_EXTENSION + " should not be visible as it's supported.");
179 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION));
180 | assertFalse(page.locator(elementExtensionByXpath.formatted(QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION)).isVisible(),
181 | "The extension " + QUARKUS_LANGCHAIN4J_OPENAI_EXTENSION + " should not be visible as it's supported.");
182 |
183 | // Check that the unsupported extension is visible
184 | LOGGER.info("Trying to find element: " + elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION));
185 | assertTrue(page.locator(elementExtensionByXpath.formatted(QUARKUS_REST_LINKS_EXTENSION)).isVisible(),
186 | "The extension " + QUARKUS_REST_LINKS_EXTENSION + " should be visible as it isn't supported.");
187 | }
188 |
189 | @Test
190 | public void checkAllSupportOptionArePresent(TestInfo testInfo) {
191 | String supportButtonXpath = "//button[@aria-label=\"%s\"]";
192 | String supportDivXpath = "//div[@aria-label=\"Add support:%s filter\"]";
193 | // TODO add `dev-preview` when the code.quarkus is updated
194 | String[] supportScopes = {"full-support", "tech-preview", "dev-support", "supported-in-jvm", "deprecated"};
195 |
196 | Page page = loadPage(webPageUrl, 60);
197 | // The support menu is not visible without clicking at it
198 | closeIntroductionModalWindow(page);
199 | page.locator(elementSupportFilterXpath).click();
200 |
201 | LOGGER.info("Trying to find element: " + supportButtonXpath.formatted("Add has support filter"));
202 | assertTrue(page.locator(supportButtonXpath.formatted("Add has support filter")).isVisible(),
203 | "The option to show all supported extensions should be present under the support filter.");
204 | LOGGER.info("Trying to find element: " + supportButtonXpath.formatted("Add no support filter"));
205 | assertTrue(page.locator(supportButtonXpath.formatted("Add no support filter")).isVisible(),
206 | "The option to show all unsupported extensions should be present under the support filter.");
207 |
208 | for (String supportScope : supportScopes) {
209 | LOGGER.info("Trying to find element: " + supportDivXpath.formatted(supportScope));
210 | assertTrue(page.locator(supportDivXpath.formatted(supportScope)).isVisible(),
211 | "The support scope " + supportScope + "should be present under the support filter.");
212 | }
213 | }
214 |
215 | public void closeIntroductionModalWindow(Page page) {
216 | page.locator(elementIntroductionModalWindowXpath).click();
217 | }
218 |
219 | public void showAllExtension(Page page){
220 | // It's needed to show all extension, otherwise all filtered list of extension is not visible
221 | closeIntroductionModalWindow(page);
222 | page.locator(elementExpandExtensionXpath).click();
223 | }
224 |
225 | public Page loadPage(String url, int timeoutSeconds) {
226 | return loadPageSettingWaitSelector(url, timeoutSeconds, pageLoadedSelector);
227 | }
228 |
229 | public Page loadPageWithExtensions(String url, int timeoutSeconds) {
230 | return loadPageSettingWaitSelector(url, timeoutSeconds, pageWithExtensionLoadedSelector);
231 | }
232 |
233 | public Page loadPageSettingWaitSelector(String url, int timeoutSeconds, String selector) {
234 | LOGGER.info("Loading web page " + url);
235 | Page page = browserContext.newPage(); // this will create new tab inside browser
236 | page.setDefaultTimeout(timeoutSeconds * 1000);
237 | page.navigate(url);
238 | page.waitForSelector(selector);
239 | return page;
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/StartStopTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop;
2 |
3 | import io.quarkus.ts.startstop.utils.Apps;
4 | import io.quarkus.ts.startstop.utils.AsyncProfiler;
5 | import io.quarkus.ts.startstop.utils.Commands;
6 | import io.quarkus.ts.startstop.utils.LogBuilder;
7 | import io.quarkus.ts.startstop.utils.Logs;
8 | import io.quarkus.ts.startstop.utils.MvnCmds;
9 | import io.quarkus.ts.startstop.utils.OpenTelemetryCollector;
10 | import io.quarkus.ts.startstop.utils.UnitTestResource;
11 | import io.quarkus.ts.startstop.utils.WebpageTester;
12 | import org.apache.commons.io.FileUtils;
13 | import org.jboss.logging.Logger;
14 | import org.junit.jupiter.api.Tag;
15 | import org.junit.jupiter.api.Test;
16 | import org.junit.jupiter.api.TestInfo;
17 |
18 | import java.io.File;
19 | import java.io.IOException;
20 | import java.nio.file.Files;
21 | import java.nio.file.Path;
22 | import java.nio.file.Paths;
23 | import java.util.ArrayList;
24 | import java.util.Arrays;
25 | import java.util.Collections;
26 | import java.util.Date;
27 | import java.util.List;
28 | import java.util.Optional;
29 | import java.util.concurrent.ExecutorService;
30 | import java.util.concurrent.Executors;
31 | import java.util.concurrent.TimeUnit;
32 | import java.util.function.Supplier;
33 |
34 | import static io.quarkus.ts.startstop.utils.Commands.cleanTarget;
35 | import static io.quarkus.ts.startstop.utils.Commands.dropCaches;
36 | import static io.quarkus.ts.startstop.utils.Commands.getBaseDir;
37 | import static io.quarkus.ts.startstop.utils.Commands.getBuildCommand;
38 | import static io.quarkus.ts.startstop.utils.Commands.getOpenedFDs;
39 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusGroupId;
40 | import static io.quarkus.ts.startstop.utils.Commands.getQuarkusVersion;
41 | import static io.quarkus.ts.startstop.utils.Commands.getRSSkB;
42 | import static io.quarkus.ts.startstop.utils.Commands.getRunCommand;
43 | import static io.quarkus.ts.startstop.utils.Commands.parsePort;
44 | import static io.quarkus.ts.startstop.utils.Commands.processStopper;
45 | import static io.quarkus.ts.startstop.utils.Commands.runCommand;
46 | import static io.quarkus.ts.startstop.utils.Commands.waitForTcpClosed;
47 | import static io.quarkus.ts.startstop.utils.Commands.disableCleanup;
48 | import static io.quarkus.ts.startstop.utils.Logs.SKIP;
49 | import static io.quarkus.ts.startstop.utils.Logs.appendln;
50 | import static io.quarkus.ts.startstop.utils.Logs.appendlnSection;
51 | import static io.quarkus.ts.startstop.utils.Logs.archiveLog;
52 | import static io.quarkus.ts.startstop.utils.Logs.checkListeningHost;
53 | import static io.quarkus.ts.startstop.utils.Logs.checkLog;
54 | import static io.quarkus.ts.startstop.utils.Logs.checkThreshold;
55 | import static io.quarkus.ts.startstop.utils.Logs.getLogsDir;
56 | import static io.quarkus.ts.startstop.utils.Logs.parseStartStopTimestamps;
57 | import static io.quarkus.ts.startstop.utils.Logs.writeReport;
58 | import static io.quarkus.ts.startstop.utils.RunCommandAugmentor.setCommandPrefix;
59 | import static io.quarkus.ts.startstop.utils.RunCommandAugmentor.setMemoryLimits;
60 | import static org.junit.jupiter.api.Assertions.assertTrue;
61 |
62 | /**
63 | * Tests for build and start of applications with some real source code
64 | * Detains in https://github.com/quarkus-qe/quarkus-startstop#startstoptest
65 | */
66 | @Tag("startstop")
67 | public class StartStopTest {
68 |
69 | private static final Logger LOGGER = Logger.getLogger(StartStopTest.class.getName());
70 |
71 | public static final String BASE_DIR = getBaseDir();
72 |
73 | public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds) throws IOException, InterruptedException {
74 | testRuntime(testInfo, app, mvnCmds, UnitTestResource.NOOP_SUPPLIER);
75 | }
76 |
77 | public void testRuntime(TestInfo testInfo, Apps app, MvnCmds mvnCmds, Supplier testResourceSupplier) throws IOException, InterruptedException {
78 | LOGGER.info("Testing app: " + app.toString() + ", mode: " + mvnCmds.toString());
79 | LOGGER.info("Cleanup Enabled: " + !disableCleanup());
80 |
81 | Process pA = null;
82 | File buildLogA = null;
83 | File runLogA = null;
84 | StringBuilder whatIDidReport = new StringBuilder();
85 | File appDir = new File(BASE_DIR + File.separator + app.dir);
86 | Optional asyncProfiler = mvnCmds == MvnCmds.JVM ? AsyncProfiler.create() : Optional.empty();
87 | String canonicalName = testInfo.getTestClass().get().getCanonicalName();
88 | String methodName = testInfo.getTestMethod().get().getName();
89 | try (var testResource = testResourceSupplier.get()) {
90 | // Cleanup
91 | asyncProfiler.ifPresent(ignore -> AsyncProfiler.cleanProfilingResults(app));
92 | cleanTarget(app);
93 | Files.createDirectories(Paths.get(appDir.getAbsolutePath() + File.separator + "logs"));
94 |
95 | // Build
96 | buildLogA = new File(appDir.getAbsolutePath() + File.separator + "logs" + File.separator + mvnCmds.name().toLowerCase() + "-build.log");
97 | ExecutorService buildService = Executors.newFixedThreadPool(1);
98 |
99 | List baseBuildCmd = new ArrayList<>();
100 | baseBuildCmd.addAll(Arrays.asList(mvnCmds.mvnCmds[0]));
101 | baseBuildCmd.add("-Dquarkus.version=" + getQuarkusVersion());
102 | baseBuildCmd.add("-Dquarkus.platform.group-id=" + getQuarkusGroupId());
103 | final List buildCommand = getBuildCommand(baseBuildCmd.toArray(new String[0]));
104 | LOGGER.info("Running " + baseBuildCmd + " in the " + appDir.getAbsolutePath());
105 |
106 | buildService.submit(new Commands.ProcessRunner(appDir, buildLogA, buildCommand, 20));
107 | appendln(whatIDidReport, "# " + canonicalName + ", " + methodName);
108 | appendln(whatIDidReport, (new Date()).toString());
109 | appendln(whatIDidReport, appDir.getAbsolutePath());
110 | appendlnSection(whatIDidReport, String.join(" ", buildCommand));
111 | long buildStarts = System.currentTimeMillis();
112 | buildService.shutdown();
113 | buildService.awaitTermination(30, TimeUnit.MINUTES);
114 | long buildEnds = System.currentTimeMillis();
115 |
116 | assertTrue(buildLogA.exists());
117 | boolean skipLogCheck = Boolean.getBoolean("start-stop.skip.log-check");
118 | if (!skipLogCheck) {
119 | checkLog(canonicalName, methodName, app, mvnCmds, buildLogA);
120 | }
121 |
122 | boolean isNative = mvnCmds == MvnCmds.NATIVE;
123 | if (isNative) {
124 | String nativeBinaryLocation = mvnCmds.mvnCmds[1][0];
125 | Path nativeBinaryPath = Paths.get(appDir.getAbsolutePath(), nativeBinaryLocation);
126 | appendln(whatIDidReport, "Native binary path: " + nativeBinaryPath);
127 | long bytes = Files.size(nativeBinaryPath);
128 | String prettySize = FileUtils.byteCountToDisplaySize(bytes);
129 | LOGGER.info("Native binary SIZE: " + prettySize);
130 | appendlnSection(whatIDidReport, " SIZE: " + prettySize);
131 | }
132 |
133 | List rssKbList = new ArrayList<>(10);
134 | List timeToFirstOKRequestList = new ArrayList<>(10);
135 | int iterations = Integer.getInteger("start-stop.iterations", 10);
136 | boolean coldStart = Boolean.getBoolean("start-stop.cold-start");
137 | boolean skipThresholdCheck = Boolean.getBoolean("start-stop.skip.threshold-check");
138 | List commandPrefix = getSystemPropertyAsList("start-stop.command.prefix", "");
139 | List jvmMemory = getSystemPropertyAsList("start-stop.jvm.memory", "-Xmx256m");
140 | List nativeMemory = getSystemPropertyAsList("start-stop.native.memory", "-Xmx96m");
141 | for (int i = 0; i < iterations; i++) {
142 | // Run
143 | LOGGER.info("Running... round " + i);
144 | runLogA = new File(appDir.getAbsolutePath() + File.separator + "logs" + File.separator + mvnCmds.name().toLowerCase() + "-run.log");
145 | appendln(whatIDidReport, appDir.getAbsolutePath());
146 | var runCommand = asyncProfiler.map(control -> control.createJavaProfiledRunCommand(mvnCmds.mvnCmds[1]))
147 | .orElseGet(() -> getRunCommand(mvnCmds.mvnCmds[1]));
148 |
149 | runCommand = setCommandPrefix(runCommand, commandPrefix);
150 | runCommand = setMemoryLimits(runCommand, isNative ? nativeMemory:jvmMemory, isNative);
151 |
152 | appendlnSection(whatIDidReport, String.join(" ", runCommand));
153 | if (coldStart) {
154 | LOGGER.info("Using COLD start");
155 | dropCaches();
156 | }
157 | pA = runCommand(runCommand, appDir, runLogA);
158 |
159 | // Test web pages
160 | long timeToFirstOKRequest = WebpageTester.testWeb(app.urlContent.urlContent[0][0], 10, app.urlContent.urlContent[0][1], true);
161 |
162 | final Process currentProcess = pA;
163 | final int runId = i;
164 | asyncProfiler.ifPresent(control -> control.stopProfing(appDir, mvnCmds, currentProcess, runId));
165 |
166 | LOGGER.info("Testing web page content...");
167 | for (String[] urlContent : app.urlContent.urlContent) {
168 | WebpageTester.testWeb(urlContent[0], 5, urlContent[1], false);
169 | }
170 |
171 | LOGGER.info("Terminate and scan logs...");
172 | pA.getInputStream().available();
173 |
174 | long rssKb = getRSSkB(pA.pid());
175 | long openedFiles = getOpenedFDs(pA.pid());
176 |
177 | processStopper(pA, false);
178 |
179 | LOGGER.info("Gonna wait for ports closed...");
180 | // Release ports
181 | assertTrue(waitForTcpClosed("localhost", parsePort(app.urlContent.urlContent[0][0]), 60),
182 | "Main port is still open");
183 | if (!skipLogCheck) {
184 | checkLog(canonicalName, methodName, app, mvnCmds, runLogA);
185 | }
186 | checkListeningHost(canonicalName, methodName, mvnCmds, runLogA);
187 |
188 | if (commandPrefix.size() > 0) {
189 | // unfortunately some active wait is needed to get stop message into logs
190 | Thread.sleep(1000l);
191 | }
192 | float[] startedStopped = parseStartStopTimestamps(runLogA);
193 |
194 | Path measurementsLog = Paths.get(getLogsDir(canonicalName, methodName).toString(), "measurements.csv");
195 | LogBuilder.Log log = new LogBuilder()
196 | .app(app)
197 | .mode(mvnCmds)
198 | .buildTimeMs(buildEnds - buildStarts)
199 | .timeToFirstOKRequestMs(timeToFirstOKRequest)
200 | .startedInMs((long) (startedStopped[0] * 1000))
201 | .stoppedInMs((long) (startedStopped[1] * 1000))
202 | .rssKb(rssKb)
203 | .openedFiles(openedFiles)
204 | .build();
205 | Logs.logMeasurements(log, measurementsLog);
206 | appendln(whatIDidReport, "Measurements:");
207 | appendln(whatIDidReport, log.headerMarkdown + "\n" + log.lineMarkdown);
208 |
209 | rssKbList.add(rssKb);
210 | timeToFirstOKRequestList.add(timeToFirstOKRequest);
211 |
212 | testResource.reset();
213 | }
214 | LOGGER.info("Calculating the stats");
215 | long rssKbAvgWithoutMinMax = getAvgWithoutMinMax(rssKbList);
216 | long timeToFirstOKRequestAvgWithoutMinMax = getAvgWithoutMinMax(timeToFirstOKRequestList);
217 | LOGGER.info("AVG timeToFirstOKRequest (ms) without min and max values: " + timeToFirstOKRequestAvgWithoutMinMax);
218 | LOGGER.info("AVG RSS (kB) without min and max values: " + rssKbAvgWithoutMinMax);
219 | if (!skipThresholdCheck) {
220 | checkThreshold(app, mvnCmds, rssKbAvgWithoutMinMax, timeToFirstOKRequestAvgWithoutMinMax, SKIP);
221 | }
222 | } finally {
223 | // Make sure processes are down even if there was an exception / failure
224 | if (pA != null) {
225 | processStopper(pA, true);
226 | }
227 | asyncProfiler.ifPresent(profiler -> profiler.archiveProfilingResults(canonicalName, methodName, appDir));
228 | archiveLog(canonicalName, methodName, buildLogA);
229 | archiveLog(canonicalName, methodName, runLogA);
230 | writeReport(canonicalName, methodName, whatIDidReport.toString());
231 | if (!disableCleanup()) {
232 | cleanTarget(app);
233 | }
234 | }
235 | }
236 |
237 | private static List getSystemPropertyAsList(String key, String def) {
238 | String memoryString = System.getProperty(key, def);
239 | return Arrays.asList(memoryString.split(" "));
240 | }
241 |
242 | private long getAvgWithoutMinMax(List listOfValues) {
243 | listOfValues.remove(Collections.min(listOfValues));
244 | listOfValues.remove(Collections.max(listOfValues));
245 | return (long) listOfValues.stream().mapToLong(val -> val).average().orElse(Long.MAX_VALUE);
246 | }
247 |
248 | @Test
249 | public void jakartaRESTMinimalJVM(TestInfo testInfo) throws IOException, InterruptedException {
250 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.JVM);
251 | }
252 |
253 | @Test
254 | @Tag("native")
255 | public void jakartaRESTMinimalNative(TestInfo testInfo) throws IOException, InterruptedException {
256 | testRuntime(testInfo, Apps.JAKARTA_REST_MINIMAL, MvnCmds.NATIVE);
257 | }
258 |
259 | @Test
260 | public void fullMicroProfileJVM(TestInfo testInfo) throws IOException, InterruptedException {
261 | testRuntime(testInfo, Apps.FULL_MICROPROFILE, MvnCmds.JVM, OpenTelemetryCollector::new);
262 | }
263 |
264 | @Test
265 | @Tag("native")
266 | public void fullMicroProfileNative(TestInfo testInfo) throws IOException, InterruptedException {
267 | testRuntime(testInfo, Apps.FULL_MICROPROFILE, MvnCmds.NATIVE, OpenTelemetryCollector::new);
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/testsuite/src/it/java/io/quarkus/ts/startstop/CodeQuarkusTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkus.ts.startstop;
2 |
3 | import io.quarkus.ts.startstop.utils.Apps;
4 | import io.quarkus.ts.startstop.utils.CodeQuarkusExtensions;
5 | import io.quarkus.ts.startstop.utils.Commands;
6 | import io.quarkus.ts.startstop.utils.MvnCmds;
7 | import io.quarkus.ts.startstop.utils.URLContent;
8 | import io.quarkus.ts.startstop.utils.WebpageTester;
9 |
10 | import org.apache.commons.lang3.StringUtils;
11 | import org.jboss.logging.Logger;
12 | import org.junit.jupiter.api.Tag;
13 | import org.junit.jupiter.api.Test;
14 | import org.junit.jupiter.api.TestInfo;
15 | import org.junit.jupiter.params.ParameterizedTest;
16 | import org.junit.jupiter.params.provider.MethodSource;
17 |
18 | import java.io.File;
19 | import java.nio.file.Files;
20 | import java.nio.file.Paths;
21 | import java.util.ArrayList;
22 | import java.util.Arrays;
23 | import java.util.Date;
24 | import java.util.List;
25 | import java.util.concurrent.ExecutorService;
26 | import java.util.concurrent.Executors;
27 | import java.util.concurrent.TimeUnit;
28 | import java.util.stream.Stream;
29 |
30 | import static io.quarkus.ts.startstop.utils.Commands.adjustPrettyPrintForJsonLogging;
31 | import static io.quarkus.ts.startstop.utils.Commands.cleanDirOrFile;
32 | import static io.quarkus.ts.startstop.utils.Commands.confIndexPageForSkeleton;
33 | import static io.quarkus.ts.startstop.utils.Commands.disableDevServices;
34 | import static io.quarkus.ts.startstop.utils.Commands.download;
35 | import static io.quarkus.ts.startstop.utils.Commands.dropEntityAnnotations;
36 | import static io.quarkus.ts.startstop.utils.Commands.getArtifactGeneBaseDir;
37 | import static io.quarkus.ts.startstop.utils.Commands.getBuildCommand;
38 | import static io.quarkus.ts.startstop.utils.Commands.getRunCommand;
39 | import static io.quarkus.ts.startstop.utils.Commands.parsePort;
40 | import static io.quarkus.ts.startstop.utils.Commands.processStopper;
41 | import static io.quarkus.ts.startstop.utils.Commands.removeRepositoriesAndPluginRepositories;
42 | import static io.quarkus.ts.startstop.utils.Commands.runCommand;
43 | import static io.quarkus.ts.startstop.utils.Commands.unzip;
44 | import static io.quarkus.ts.startstop.utils.Commands.waitForTcpClosed;
45 | import static io.quarkus.ts.startstop.utils.Logs.appendln;
46 | import static io.quarkus.ts.startstop.utils.Logs.appendlnSection;
47 | import static io.quarkus.ts.startstop.utils.Logs.archiveLog;
48 | import static io.quarkus.ts.startstop.utils.Logs.checkListeningHost;
49 | import static io.quarkus.ts.startstop.utils.Logs.checkLog;
50 | import static io.quarkus.ts.startstop.utils.Logs.writeReport;
51 | import static org.junit.jupiter.api.Assertions.assertTrue;
52 |
53 | /**
54 | * Tests for code.quarkus.io generator
55 | */
56 | @Tag("codequarkus")
57 | public class CodeQuarkusTest {
58 |
59 | private static final Logger LOGGER = Logger.getLogger(CodeQuarkusTest.class.getName());
60 |
61 | public static final String GEN_BASE_DIR = getArtifactGeneBaseDir();
62 |
63 | public static final List> supportedEx = CodeQuarkusExtensions.partition(4, CodeQuarkusExtensions.Flag.SUPPORTED);
64 | public static final List> notSupportedEx = CodeQuarkusExtensions.partition(1, CodeQuarkusExtensions.Flag.NOT_SUPPORTED);
65 | public static final List> mixedEx = CodeQuarkusExtensions.partition(1, CodeQuarkusExtensions.Flag.MIXED);
66 |
67 | public static final Stream> supportedExWithCodeStarter() {
68 | return Arrays.asList(
69 | Arrays.asList(CodeQuarkusExtensions.QUARKUS_SMALLRYE_HEALTH, CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE),
70 | Arrays.asList(CodeQuarkusExtensions.QUARKUS_LOGGING_JSON, CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE),
71 | Arrays.asList(CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE),
72 | Arrays.asList(CodeQuarkusExtensions.QUARKUS_SPRING_WEB),
73 | Arrays.asList(CodeQuarkusExtensions.QUARKUS_WEBSOCKETS)
74 | ).stream();
75 | }
76 |
77 | public void testRuntime(TestInfo testInfo, List extensions, MvnCmds mvnCmds) throws Exception {
78 | Process pA = null;
79 | File unzipLog = null;
80 | File buildLogA = null;
81 | File runLogA = null;
82 | StringBuilder whatIDidReport = new StringBuilder();
83 | String cn = testInfo.getTestClass().get().getCanonicalName();
84 | String mn = testInfo.getTestMethod().get().getName();
85 | LOGGER.info(mn + ": Testing Code Quarkus generator with these " + extensions.size() + " extensions: " + extensions.toString() + ", mode: " + mvnCmds.toString());
86 | File appDir = new File(GEN_BASE_DIR + File.separator + "code-with-quarkus");
87 | String logsDir = GEN_BASE_DIR + File.separator + "code-with-quarkus-logs";
88 | URLContent skeletonApp = Apps.GENERATED_SKELETON.urlContent;
89 | String zipFile = GEN_BASE_DIR + File.separator + "code-with-quarkus.zip";
90 |
91 | try {
92 | cleanDirOrFile(appDir.getAbsolutePath(), logsDir);
93 | Files.createDirectories(Paths.get(logsDir));
94 | appendln(whatIDidReport, "# " + cn + ", " + mn);
95 | appendln(whatIDidReport, (new Date()).toString());
96 | LOGGER.info("Downloading...");
97 | appendln(whatIDidReport, "Download URL: " + download(extensions, zipFile, 17));
98 | LOGGER.info("Unzipping...");
99 | unzipLog = unzip(zipFile, GEN_BASE_DIR);
100 | if (StringUtils.isBlank(System.getProperty("gh.actions"))) {
101 | LOGGER.info("Removing repositories and pluginRepositories from pom.xml ...");
102 | removeRepositoriesAndPluginRepositories(appDir + File.separator + "pom.xml");
103 | }
104 | adjustPrettyPrintForJsonLogging(appDir.getAbsolutePath());
105 | confIndexPageForSkeleton(appDir.getAbsolutePath());
106 | disableDevServices(appDir.getAbsolutePath());
107 | dropEntityAnnotations(appDir.getAbsolutePath());
108 | List cmd;
109 | // Build
110 | if (mvnCmds != MvnCmds.MVNW_DEV) {
111 | buildLogA = new File(logsDir + File.separator + "build.log");
112 | ExecutorService buildService = Executors.newFixedThreadPool(1);
113 | List baseBuildCmd = new ArrayList<>();
114 | baseBuildCmd.addAll(Arrays.asList(mvnCmds.mvnCmds[0]));
115 | cmd = getBuildCommand(baseBuildCmd.toArray(new String[0]));
116 |
117 | appendln(whatIDidReport, "# " + cn + ", " + mn);
118 | appendln(whatIDidReport, (new Date()).toString());
119 | appendln(whatIDidReport, appDir.getAbsolutePath());
120 | appendln(whatIDidReport, "Extensions: " + extensions.toString());
121 | appendlnSection(whatIDidReport, String.join(" ", cmd));
122 |
123 | LOGGER.info("Building (" + cmd + ")");
124 | buildService.submit(new Commands.ProcessRunner(appDir, buildLogA, cmd, 20));
125 |
126 | buildService.shutdown();
127 | buildService.awaitTermination(30, TimeUnit.MINUTES);
128 |
129 | assertTrue(buildLogA.exists());
130 | }
131 |
132 | // Run
133 | runLogA = new File(logsDir + File.separator + "dev-run.log");
134 | if (mvnCmds == MvnCmds.MVNW_DEV) {
135 | cmd = getBuildCommand(mvnCmds.mvnCmds[0]);
136 | } else {
137 | cmd = getRunCommand(mvnCmds.mvnCmds[1]);
138 | }
139 |
140 | LOGGER.info("Running (" + cmd + ") in directory: " + appDir);
141 | appendln(whatIDidReport, "Extensions: " + extensions.toString());
142 | appendln(whatIDidReport, appDir.getAbsolutePath());
143 | appendlnSection(whatIDidReport, String.join(" ", cmd));
144 |
145 | pA = runCommand(cmd, appDir, runLogA);
146 |
147 | // It takes time to download the Internet
148 | long timeoutS = 10 * 60;
149 | LOGGER.info("Timeout: " + timeoutS + "s. Waiting for the web content...");
150 | WebpageTester.testWeb(skeletonApp.urlContent[0][0], timeoutS, skeletonApp.urlContent[0][1], false);
151 | LOGGER.info("Terminating and scanning logs...");
152 | pA.getInputStream().available();
153 | processStopper(pA, false);
154 | LOGGER.info("Gonna wait for ports closed...");
155 | assertTrue(waitForTcpClosed("localhost", parsePort(skeletonApp.urlContent[0][0]), 60),
156 | "Main port is still open.");
157 | checkLog(cn, mn, Apps.GENERATED_SKELETON, mvnCmds, runLogA);
158 | checkListeningHost(cn, mn, mvnCmds, runLogA);
159 | } finally {
160 | if (pA != null) {
161 | processStopper(pA, true);
162 | }
163 |
164 | String tag = StringUtils.EMPTY;
165 | if (extensions.size() == 1) {
166 | tag = "-" + extensions.get(0).id;
167 | }
168 |
169 | archiveLog(cn, mn + tag, unzipLog);
170 | archiveLog(cn, mn + tag, buildLogA);
171 | archiveLog(cn, mn + tag, runLogA);
172 | writeReport(cn, mn + tag, whatIDidReport.toString());
173 | cleanDirOrFile(appDir.getAbsolutePath(), logsDir);
174 | }
175 | }
176 |
177 | @Test
178 | public void supportedExtensionsSubsetA(TestInfo testInfo) throws Exception {
179 | testRuntime(testInfo, supportedEx.get(0), MvnCmds.MVNW_DEV);
180 | }
181 |
182 | @Test
183 | public void supportedExtensionsSubsetB(TestInfo testInfo) throws Exception {
184 | List supportedExtensionsSubsetB = supportedEx.get(1);
185 | // resteasy-reactive extension to open port 8080 and provide index.html file
186 | supportedExtensionsSubsetB.add(CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE);
187 | testRuntime(testInfo, supportedExtensionsSubsetB, MvnCmds.MVNW_DEV);
188 | }
189 |
190 | @Test
191 | public void supportedExtensionsSubsetC(TestInfo testInfo) throws Exception {
192 | List supportedExtensionsSubsetC = supportedEx.get(2);
193 | // resteasy or spring-web extension is needed to provide index.html file
194 | // content from index.html file is checked to ensure the application is up and running
195 | supportedExtensionsSubsetC.add(CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE);
196 | testRuntime(testInfo, supportedExtensionsSubsetC, MvnCmds.MVNW_DEV);
197 | }
198 |
199 | @Test
200 | public void supportedExtensionsSubsetD(TestInfo testInfo) throws Exception {
201 | testRuntime(testInfo, supportedEx.get(3), MvnCmds.MVNW_DEV);
202 | }
203 |
204 | @Test
205 | public void supportedRestEasyClassicExtensions(TestInfo testInfo) throws Exception {
206 | testRuntime(testInfo, CodeQuarkusExtensions.getRestEasyClassicExtensions(), MvnCmds.MVNW_DEV);
207 | }
208 |
209 | @Test
210 | public void notSupportedExtensionsSubsetA(TestInfo testInfo) throws Exception {
211 | testRuntime(testInfo, notSupportedEx.get(0).subList(0, Math.min(10, notSupportedEx.get(0).size())), MvnCmds.MVNW_DEV);
212 | }
213 |
214 | @Test
215 | public void notSupportedExtensionsSubsetB(TestInfo testInfo) throws Exception {
216 | List notSupportedExtensionsSubsetB = notSupportedEx.get(0).subList(Math.min(10, notSupportedEx.get(0).size()), Math.min(20, notSupportedEx.get(0).size()));
217 | // resteasy or spring-web extension is needed to provide index.html file
218 | // content from index.html file is checked to ensure the application is up and running
219 | notSupportedExtensionsSubsetB.add(CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE);
220 | testRuntime(testInfo, notSupportedExtensionsSubsetB, MvnCmds.MVNW_DEV);
221 | }
222 |
223 | @Test
224 | public void mixExtensions(TestInfo testInfo) throws Exception {
225 | testRuntime(testInfo, mixedEx.get(0).subList(0, Math.min(20, mixedEx.get(0).size())), MvnCmds.MVNW_DEV);
226 | }
227 |
228 | @Test
229 | public void java17BasedProject(TestInfo testInfo) throws Exception {
230 | StringBuilder whatIDidReport = new StringBuilder();
231 | String cn = testInfo.getTestClass().get().getCanonicalName();
232 | String mn = testInfo.getTestMethod().get().getName();
233 | File appDir = new File(GEN_BASE_DIR + File.separator + "code-with-quarkus");
234 | String zipFile = GEN_BASE_DIR + File.separator + "code-with-quarkus.zip";
235 |
236 | try {
237 | appendln(whatIDidReport, "# " + cn + ", " + mn);
238 | appendln(whatIDidReport, (new Date()).toString());
239 | LOGGER.info("Downloading...");
240 | appendln(whatIDidReport, "Download URL: " + download(List.of(CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE), zipFile, 17));
241 | LOGGER.info("Unzipping...");
242 | unzip(zipFile, GEN_BASE_DIR);
243 |
244 | String pom = Files.readString(Paths.get(GEN_BASE_DIR + File.separator + "code-with-quarkus" + File.separator + "pom.xml"));
245 | assertTrue(pom.contains("17"), "Downloaded app doesn't have Java 17 as a default version in pom.xml");
246 |
247 | } finally {
248 | writeReport(cn, mn, whatIDidReport.toString());
249 | cleanDirOrFile(appDir.getAbsolutePath());
250 | }
251 | }
252 |
253 | /*
254 | * Similar to java21BasedProject test, but not forcing concrete java version
255 | */
256 | @Test
257 | public void defaultJavaBasedProject(TestInfo testInfo) throws Exception {
258 | StringBuilder whatIDidReport = new StringBuilder();
259 | String cn = testInfo.getTestClass().get().getCanonicalName();
260 | String mn = testInfo.getTestMethod().get().getName();
261 | File appDir = new File(GEN_BASE_DIR + File.separator + "code-with-quarkus");
262 | String zipFile = GEN_BASE_DIR + File.separator + "code-with-quarkus.zip";
263 |
264 | try {
265 | appendln(whatIDidReport, "# " + cn + ", " + mn);
266 | appendln(whatIDidReport, (new Date()).toString());
267 | LOGGER.info("Downloading...");
268 | appendln(whatIDidReport, "Download URL: " + download(List.of(CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE), zipFile));
269 | LOGGER.info("Unzipping...");
270 | unzip(zipFile, GEN_BASE_DIR);
271 |
272 | String pom = Files.readString(Paths.get(GEN_BASE_DIR + File.separator + "code-with-quarkus" + File.separator + "pom.xml"));
273 | assertTrue(pom.contains("21"), "Downloaded app doesn't have Java 21 as a default in pom.xml");
274 |
275 | } finally {
276 | writeReport(cn, mn, whatIDidReport.toString());
277 | cleanDirOrFile(appDir.getAbsolutePath());
278 | }
279 | }
280 |
281 | @ParameterizedTest
282 | @MethodSource("supportedExWithCodeStarter")
283 | public void supportedExtensionWithCodeStarterWorksInJVM(List extensions, TestInfo testInfo) throws Exception {
284 | testRuntime(testInfo, extensions, MvnCmds.MVNW_JVM);
285 | }
286 |
287 | @Tag("native")
288 | @ParameterizedTest
289 | @MethodSource("supportedExWithCodeStarter")
290 | public void supportedExtensionWithCodeStarterWorksInNative(List extensions, TestInfo testInfo) throws Exception {
291 | testRuntime(testInfo, extensions, MvnCmds.MVNW_NATIVE);
292 | }
293 |
294 | @Tag("native")
295 | @Test
296 | public void notSupportedExtensionsWorksInNative(TestInfo testInfo) throws Exception {
297 | List notSupportedExtensionsSubset = List.of(
298 | CodeQuarkusExtensions.QUARKUS_MONGODB_CLIENT, // verifies QUARKUS-3194
299 | // resteasy or spring-web extension is needed to provide index.html file
300 | // content from index.html file is checked to ensure the application is up and running
301 | CodeQuarkusExtensions.QUARKUS_RESTEASY_REACTIVE
302 | );
303 | testRuntime(testInfo, notSupportedExtensionsSubset, MvnCmds.MVNW_NATIVE);
304 | }
305 | }
306 |
--------------------------------------------------------------------------------