diff --git a/.github/matrix-jvm-tests.json b/.github/matrix-jvm-tests.json index 7e901e49ad11b..29974bf6d342a 100644 --- a/.github/matrix-jvm-tests.json +++ b/.github/matrix-jvm-tests.json @@ -3,6 +3,7 @@ "name": "JVM Tests - JDK 17", "category": "Runtime", "java-version": 17, + "java-distribution": "temurin", "maven_args": "$JVM_TEST_MAVEN_ARGS", "maven_opts": "-Xmx2g -XX:MaxMetaspaceSize=1g", "os-name": "ubuntu-latest", @@ -12,6 +13,7 @@ "name": "JVM Tests - JDK 21", "category": "Runtime", "java-version": 21, + "java-distribution": "temurin", "java-version-gradle": 17, "maven_args": "$JVM_TEST_MAVEN_ARGS", "maven_opts": "-Xmx3g -XX:MaxMetaspaceSize=1g", @@ -22,6 +24,7 @@ "name": "JVM Tests - JDK 25", "category": "Runtime", "java-version": 25, + "java-distribution": "temurin", "java-version-gradle": 17, "maven_args": "$JVM_TEST_MAVEN_ARGS", "maven_opts": "-Xmx3g -XX:MaxMetaspaceSize=1g", @@ -32,15 +35,28 @@ "name": "JVM Tests - JDK 17 Windows", "category": "Runtime", "java-version": 17, + "java-distribution": "temurin", "maven_args": "-DskipDocs -Dformat.skip", "maven_opts": "-Xmx2g -XX:MaxMetaspaceSize=1g", "os-name": "windows-latest", "modules": "-pl\n!docs\n-Dno-test-modules" }, + { + "name": "JVM Tests - JDK 21 Semeru", + "category": "Runtime", + "java-version": 21, + "java-distribution": "semeru", + "java-version-gradle": 17, + "maven_args": "$JVM_TEST_MAVEN_ARGS", + "maven_opts": "-Xmx3g -XX:MaxMetaspaceSize=1g", + "os-name": "ubuntu-latest", + "modules": "-pl\n!docs\n-Dno-test-modules" + }, { "name": "JVM Integration Tests - JDK 17", "category": "Integration", "java-version": 17, + "java-distribution": "temurin", "maven_args": "$JVM_TEST_MAVEN_ARGS", "maven_opts": "-Xmx2g -XX:MaxMetaspaceSize=1g", "os-name": "ubuntu-latest", @@ -50,6 +66,7 @@ "name": "JVM Integration Tests - JDK 21", "category": "Integration", "java-version": 21, + "java-distribution": "temurin", "maven_args": "$JVM_TEST_MAVEN_ARGS", "maven_opts": "-Xmx3g -XX:MaxMetaspaceSize=1g", "os-name": "ubuntu-latest", @@ -59,6 +76,7 @@ "name": "JVM Integration Tests - JDK 25", "category": "Integration", "java-version": 25, + "java-distribution": "temurin", "maven_args": "$JVM_TEST_MAVEN_ARGS", "maven_opts": "-Xmx3g -XX:MaxMetaspaceSize=1g", "os-name": "ubuntu-latest", @@ -68,9 +86,20 @@ "name": "JVM Integration Tests - JDK 17 Windows", "category": "Integration", "java-version": 17, + "java-distribution": "temurin", "maven_args": "-DskipDocs -Dformat.skip", "maven_opts": "-Xmx2g -XX:MaxMetaspaceSize=1g", "os-name": "windows-latest", "modules": "-f\nintegration-tests\n-pl\n!gradle\n-pl\n!maven\n-pl\n!devmode\n-pl\n!devtools" + }, + { + "name": "JVM Integration Tests - JDK 21 Semeru", + "category": "Integration", + "java-version": 21, + "java-distribution": "semeru", + "maven_args": "$JVM_TEST_MAVEN_ARGS", + "maven_opts": "-Xmx3g -XX:MaxMetaspaceSize=1g", + "os-name": "ubuntu-latest", + "modules": "-f\nintegration-tests\n-pl\n!gradle\n-pl\n!maven\n-pl\n!devmode\n-pl\n!devtools" } ] diff --git a/.github/workflows/ci-actions-incremental.yml b/.github/workflows/ci-actions-incremental.yml index 01512b7d1bf37..909d8496fd040 100644 --- a/.github/workflows/ci-actions-incremental.yml +++ b/.github/workflows/ci-actions-incremental.yml @@ -604,15 +604,24 @@ jobs: - { name: "17", java-version: 17, + java-distribution: "temurin", os-name: "ubuntu-latest", tag: "maven-jdk-17" } - { name: "17 Windows", java-version: 17, + java-distribution: "temurin", os-name: "windows-latest", tag: "maven-jdk-17-windows" } + - { + name: "21 Semeru", + java-version: 21, + java-distribution: "semeru", + os-name: "ubuntu-latest", + tag: "maven-jdk-21-semeru" + } steps: - uses: runs-on/action@v2 - name: Gradle Enterprise environment @@ -646,7 +655,7 @@ jobs: - name: Set up JDK ${{ matrix.java.java-version }} uses: actions/setup-java@v5 with: - distribution: temurin + distribution: ${{ matrix.java.java-distribution || 'temurin' }} java-version: ${{ matrix.java.java-version }} - name: Setup Develocity Build Scan capture uses: gradle/develocity-actions/setup-maven@v1.4 @@ -809,21 +818,31 @@ jobs: - { name: "17", java-version: 17, + java-distribution: "temurin", os-name: "ubuntu-latest", tag: "devtools-jdk-17" } - { name: "21", java-version: 21, + java-distribution: "temurin", os-name: "ubuntu-latest", tag: "devtools-jdk-21" } - { name: "17 Windows", java-version: 17, + java-distribution: "temurin", os-name: "windows-latest", tag: "devtools-jdk-17-windows" } + - { + name: "21 Semeru", + java-version: 21, + java-distribution: "semeru", + os-name: "ubuntu-latest", + tag: "devtools-jdk-21-semeru" + } steps: - uses: runs-on/action@v2 - name: Gradle Enterprise environment @@ -852,7 +871,7 @@ jobs: - name: Set up JDK ${{ matrix.java.java-version }} uses: actions/setup-java@v5 with: - distribution: temurin + distribution: ${{ matrix.java.java-distribution || 'temurin' }} java-version: ${{ matrix.java.java-version }} - name: Setup Develocity Build Scan capture uses: gradle/develocity-actions/setup-maven@v1.4 @@ -913,20 +932,30 @@ jobs: name: "17", java-version: 17, os-name: "ubuntu-latest", + java-distribution: "temurin", tag: "kubernetes-jdk-17" } - { name: "21", java-version: 21, + java-distribution: "temurin", os-name: "ubuntu-latest", tag: "kubernetes-jdk-21" } - { name: "17 Windows", java-version: 17, + java-distribution: "temurin", os-name: "windows-latest", tag: "kubernetes-jdk-17-windows" } + - { + name: "21 Semeru", + java-version: 21, + java-distribution: "semeru", + os-name: "ubuntu-latest", + tag: "kubernetes-jdk-21-semeru" + } steps: - uses: runs-on/action@v2 - name: Gradle Enterprise environment @@ -955,7 +984,7 @@ jobs: - name: Set up JDK ${{ matrix.java.java-version }} uses: actions/setup-java@v5 with: - distribution: temurin + distribution: ${{ matrix.java.java-distribution || 'temurin' }} java-version: ${{ matrix.java.java-version }} - name: Setup Develocity Build Scan capture uses: gradle/develocity-actions/setup-maven@v1.4 diff --git a/extensions/jfr/deployment/pom.xml b/extensions/jfr/deployment/pom.xml index 0cf61e5aa8530..51c183e26e533 100644 --- a/extensions/jfr/deployment/pom.xml +++ b/extensions/jfr/deployment/pom.xml @@ -57,4 +57,19 @@ + + + + skip-tests-on-semeru + + + java.vendor + IBM Corporation + + + + true + + + diff --git a/extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/JfrDevModeTest.java b/extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/JfrDevModeTest.java deleted file mode 100644 index 6ddb8fb5d4db7..0000000000000 --- a/extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/JfrDevModeTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.quarkus.jfr.test; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.test.QuarkusDevModeTest; - -public class JfrDevModeTest { - - // Start hot reload (DevMode) test with your extension loaded - @RegisterExtension - static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); - - @Test - public void writeYourOwnDevModeTest() { - // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information - Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); - } -} diff --git a/extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/JfrTest.java b/extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/JfrTest.java deleted file mode 100644 index 48bbba92e23eb..0000000000000 --- a/extensions/jfr/deployment/src/test/java/io/quarkus/jfr/test/JfrTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.quarkus.jfr.test; - -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.spec.JavaArchive; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -import io.quarkus.test.QuarkusUnitTest; - -public class JfrTest { - - // Start unit test with your extension loaded - @RegisterExtension - static final QuarkusUnitTest unitTest = new QuarkusUnitTest() - .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); - - @Test - public void writeYourOwnUnitTest() { - // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information - Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); - } -} diff --git a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/params/RouteMethodParametersTest.java b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/params/RouteMethodParametersTest.java index 1028975c20a17..400f4a4647f65 100644 --- a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/params/RouteMethodParametersTest.java +++ b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/params/RouteMethodParametersTest.java @@ -13,6 +13,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.DisabledOnSemeru; import io.quarkus.test.QuarkusUnitTest; import io.quarkus.vertx.web.Body; import io.quarkus.vertx.web.Header; @@ -26,6 +27,7 @@ import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +@DisabledOnSemeru(reason = "https://github.com/eclipse-openj9/openj9/issues/22812") public class RouteMethodParametersTest { @RegisterExtension diff --git a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/MultiValidationTest.java b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/MultiValidationTest.java index 5a29bc5a147ff..9f5e7d90797d2 100644 --- a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/MultiValidationTest.java +++ b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/MultiValidationTest.java @@ -12,12 +12,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.DisabledOnSemeru; import io.quarkus.test.QuarkusUnitTest; import io.quarkus.vertx.web.Param; import io.quarkus.vertx.web.Route; import io.quarkus.vertx.web.Route.HttpMethod; import io.smallrye.mutiny.Multi; +@DisabledOnSemeru(reason = "https://github.com/eclipse-openj9/openj9/issues/22812") public class MultiValidationTest { @RegisterExtension diff --git a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/SyncValidationTest.java b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/SyncValidationTest.java index 7d49f31d1c631..b6d1fc82a5696 100644 --- a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/SyncValidationTest.java +++ b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/SyncValidationTest.java @@ -17,11 +17,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.DisabledOnSemeru; import io.quarkus.test.QuarkusUnitTest; import io.quarkus.vertx.web.Param; import io.quarkus.vertx.web.Route; import io.quarkus.vertx.web.Route.HttpMethod; +@DisabledOnSemeru(reason = "https://github.com/eclipse-openj9/openj9/issues/22812") public class SyncValidationTest { @RegisterExtension diff --git a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/UniValidationTest.java b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/UniValidationTest.java index 7ca22f2527ada..f1a7b2ebb6020 100644 --- a/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/UniValidationTest.java +++ b/extensions/reactive-routes/deployment/src/test/java/io/quarkus/vertx/web/validation/UniValidationTest.java @@ -17,12 +17,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import io.quarkus.test.DisabledOnSemeru; import io.quarkus.test.QuarkusUnitTest; import io.quarkus.vertx.web.Param; import io.quarkus.vertx.web.Route; import io.quarkus.vertx.web.Route.HttpMethod; import io.smallrye.mutiny.Uni; +@DisabledOnSemeru(reason = "https://github.com/eclipse-openj9/openj9/issues/22812") public class UniValidationTest { @RegisterExtension diff --git a/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/Collector.java b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/Collector.java index deec6ed17a3ca..381e610d05627 100644 --- a/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/Collector.java +++ b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/Collector.java @@ -1,183 +1,34 @@ package io.quarkus.test.junit5.virtual.internal; -import java.util.ArrayList; import java.util.List; -import java.util.UUID; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import java.util.function.Function; -import java.util.logging.Level; -import java.util.logging.Logger; -import io.smallrye.common.annotation.SuppressForbidden; import jdk.jfr.consumer.RecordedEvent; -import jdk.jfr.consumer.RecordingStream; -public class Collector implements Consumer { - public static final String CARRIER_PINNED_EVENT_NAME = "jdk.VirtualThreadPinned"; - private static final Logger LOGGER = Logger.getLogger(Collector.class.getName()); +public interface Collector extends Consumer { - private final List> observers = new CopyOnWriteArrayList<>(); + String CARRIER_PINNED_EVENT_NAME = "jdk.VirtualThreadPinned"; - private final List events = new CopyOnWriteArrayList<>(); - - private final RecordingStream recordingStream; - - volatile State state = State.INIT; - - public Collector() { - recordingStream = new RecordingStream(); - recordingStream.enable(CARRIER_PINNED_EVENT_NAME).withStackTrace(); - recordingStream.enable(InternalEvents.SHUTDOWN_EVENT_NAME).withoutStackTrace(); - recordingStream.enable(InternalEvents.CAPTURING_STARTED_EVENT_NAME).withoutStackTrace(); - recordingStream.enable(InternalEvents.CAPTURING_STOPPED_EVENT_NAME).withoutStackTrace(); - recordingStream.enable(InternalEvents.INITIALIZATION_EVENT_NAME).withoutStackTrace(); - recordingStream.setOrdered(true); - recordingStream.setMaxSize(100); - recordingStream.onEvent(this); - } - - @SuppressForbidden(reason = "java.util.logging is authorized here") - public void init() { - long begin = System.nanoTime(); - CountDownLatch latch = new CountDownLatch(1); - observers.add(re -> { - if (re.getEventType().getName().equals(InternalEvents.INITIALIZATION_EVENT_NAME)) { - latch.countDown(); - return true; - } - return false; - }); - recordingStream.startAsync(); - new InternalEvents.InitializationEvent().commit(); - try { - if (latch.await(10, TimeUnit.SECONDS)) { - long end = System.nanoTime(); - state = State.STARTED; - LOGGER.log(Level.FINE, "Event collection started in {0}s", (end - begin) / 1000000); - } else { - throw new IllegalStateException( - "Unable to start JFR collection, RecordingStartedEvent event not received after 10s"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); + static Collector create() { + if (Boolean.getBoolean("jfr.unsupported.vm")) { + return new NoJfrCollector(); } - } - - @SuppressForbidden(reason = "java.util.logging is authorized here") - public void start() { - CountDownLatch latch = new CountDownLatch(1); - String id = UUID.randomUUID().toString(); - long begin = System.nanoTime(); - observers.add(re -> { - if (re.getEventType().getName().equals(InternalEvents.CAPTURING_STARTED_EVENT_NAME)) { - if (id.equals(re.getString("id"))) { - events.clear(); - state = State.COLLECTING; - latch.countDown(); - return true; - } - } - return false; - }); - new InternalEvents.CapturingStartedEvent(id).commit(); - - try { - if (!latch.await(10, TimeUnit.SECONDS)) { - throw new IllegalStateException("Unable to start JFR collection, START_EVENT event not received after 10s"); - } - long end = System.nanoTime(); - LOGGER.log(Level.FINE, "Event capturing started in {0}s", (end - begin) / 1000000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } + return new JfrCollector(); } - @SuppressForbidden(reason = "java.util.logging is authorized here") - public List stop() { - CountDownLatch latch = new CountDownLatch(1); - String id = UUID.randomUUID().toString(); - var begin = System.nanoTime(); - observers.add(re -> { - if (re.getEventType().getName().equals(InternalEvents.CAPTURING_STOPPED_EVENT_NAME)) { - state = State.STARTED; - latch.countDown(); - return true; - } - return false; - }); + void init(); - new InternalEvents.CapturingStoppedEvent(id).commit(); + void start(); - try { - if (!latch.await(10, TimeUnit.SECONDS)) { - throw new IllegalStateException("Unable to start JFR collection, STOP_EVENT event not received after 10s"); - } - var end = System.nanoTime(); - LOGGER.log(Level.FINE, "Event collection stopped in {0}s", (end - begin) / 1000000); - return new ArrayList<>(events); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - } + List stop(); - @SuppressForbidden(reason = "java.util.logging is authorized here") - public void shutdown() { - CountDownLatch latch = new CountDownLatch(1); - var begin = System.nanoTime(); - observers.add(re -> { - if (re.getEventType().getName().equals(InternalEvents.SHUTDOWN_EVENT_NAME)) { - latch.countDown(); - return true; - } - return false; - }); - InternalEvents.ShutdownEvent event = new InternalEvents.ShutdownEvent(); - event.commit(); - try { - if (latch.await(10, TimeUnit.SECONDS)) { - state = State.INIT; - var end = System.nanoTime(); - LOGGER.log(Level.FINE, "Event collector shutdown in {0}s", (end - begin) / 1000000); - recordingStream.close(); - } else { - throw new IllegalStateException( - "Unable to stop JFR collection, RecordingStoppedEvent event not received at 10s"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - } + void shutdown(); @Override - public void accept(RecordedEvent re) { - if (state == State.COLLECTING) { - events.add(re); - } - List> toBeRemoved = new ArrayList<>(); - observers.forEach(c -> { - if (c.apply(re)) { - toBeRemoved.add(c); - } - }); - observers.removeAll(toBeRemoved); - } - - public List getEvents() { - return new ArrayList<>(events); - } + void accept(RecordedEvent re); - enum State { - INIT, - STARTED, - COLLECTING - } + List getEvents(); + boolean isRecording(); } diff --git a/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/JfrCollector.java b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/JfrCollector.java new file mode 100644 index 0000000000000..33faa5fea2f0f --- /dev/null +++ b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/JfrCollector.java @@ -0,0 +1,190 @@ +package io.quarkus.test.junit5.virtual.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +import io.smallrye.common.annotation.SuppressForbidden; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingStream; + +public class JfrCollector implements Collector { + private static final Logger LOGGER = Logger.getLogger(JfrCollector.class.getName()); + + private final List> observers = new CopyOnWriteArrayList<>(); + + private final List events = new CopyOnWriteArrayList<>(); + + private final RecordingStream recordingStream; + + volatile State state = State.INIT; + + public JfrCollector() { + recordingStream = new RecordingStream(); + recordingStream.enable(CARRIER_PINNED_EVENT_NAME).withStackTrace(); + recordingStream.enable(InternalEvents.SHUTDOWN_EVENT_NAME).withoutStackTrace(); + recordingStream.enable(InternalEvents.CAPTURING_STARTED_EVENT_NAME).withoutStackTrace(); + recordingStream.enable(InternalEvents.CAPTURING_STOPPED_EVENT_NAME).withoutStackTrace(); + recordingStream.enable(InternalEvents.INITIALIZATION_EVENT_NAME).withoutStackTrace(); + recordingStream.setOrdered(true); + recordingStream.setMaxSize(100); + recordingStream.onEvent(this); + } + + @Override + public boolean isRecording() { + return true; + } + + @SuppressForbidden(reason = "java.util.logging is authorized here") + @Override + public void init() { + long begin = System.nanoTime(); + CountDownLatch latch = new CountDownLatch(1); + observers.add(re -> { + if (re.getEventType().getName().equals(InternalEvents.INITIALIZATION_EVENT_NAME)) { + latch.countDown(); + return true; + } + return false; + }); + recordingStream.startAsync(); + new InternalEvents.InitializationEvent().commit(); + try { + if (latch.await(10, TimeUnit.SECONDS)) { + long end = System.nanoTime(); + state = State.STARTED; + LOGGER.log(Level.FINE, "Event collection started in {0}s", (end - begin) / 1000000); + } else { + throw new IllegalStateException( + "Unable to start JFR collection, RecordingStartedEvent event not received after 10s"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @SuppressForbidden(reason = "java.util.logging is authorized here") + @Override + public void start() { + CountDownLatch latch = new CountDownLatch(1); + String id = UUID.randomUUID().toString(); + long begin = System.nanoTime(); + observers.add(re -> { + if (re.getEventType().getName().equals(InternalEvents.CAPTURING_STARTED_EVENT_NAME)) { + if (id.equals(re.getString("id"))) { + events.clear(); + state = State.COLLECTING; + latch.countDown(); + return true; + } + } + return false; + }); + + new InternalEvents.CapturingStartedEvent(id).commit(); + + try { + if (!latch.await(10, TimeUnit.SECONDS)) { + throw new IllegalStateException("Unable to start JFR collection, START_EVENT event not received after 10s"); + } + long end = System.nanoTime(); + LOGGER.log(Level.FINE, "Event capturing started in {0}s", (end - begin) / 1000000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @SuppressForbidden(reason = "java.util.logging is authorized here") + @Override + public List stop() { + CountDownLatch latch = new CountDownLatch(1); + String id = UUID.randomUUID().toString(); + var begin = System.nanoTime(); + observers.add(re -> { + if (re.getEventType().getName().equals(InternalEvents.CAPTURING_STOPPED_EVENT_NAME)) { + state = State.STARTED; + latch.countDown(); + return true; + } + return false; + }); + + new InternalEvents.CapturingStoppedEvent(id).commit(); + + try { + if (!latch.await(10, TimeUnit.SECONDS)) { + throw new IllegalStateException("Unable to start JFR collection, STOP_EVENT event not received after 10s"); + } + var end = System.nanoTime(); + LOGGER.log(Level.FINE, "Event collection stopped in {0}s", (end - begin) / 1000000); + return new ArrayList<>(events); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @SuppressForbidden(reason = "java.util.logging is authorized here") + @Override + public void shutdown() { + CountDownLatch latch = new CountDownLatch(1); + var begin = System.nanoTime(); + observers.add(re -> { + if (re.getEventType().getName().equals(InternalEvents.SHUTDOWN_EVENT_NAME)) { + latch.countDown(); + return true; + } + return false; + }); + InternalEvents.ShutdownEvent event = new InternalEvents.ShutdownEvent(); + event.commit(); + try { + if (latch.await(10, TimeUnit.SECONDS)) { + state = State.INIT; + var end = System.nanoTime(); + LOGGER.log(Level.FINE, "Event collector shutdown in {0}s", (end - begin) / 1000000); + recordingStream.close(); + } else { + throw new IllegalStateException( + "Unable to stop JFR collection, RecordingStoppedEvent event not received at 10s"); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + + @Override + public void accept(RecordedEvent re) { + if (state == State.COLLECTING) { + events.add(re); + } + List> toBeRemoved = new ArrayList<>(); + observers.forEach(c -> { + if (c.apply(re)) { + toBeRemoved.add(c); + } + }); + observers.removeAll(toBeRemoved); + } + + @Override + public List getEvents() { + return new ArrayList<>(events); + } + + enum State { + INIT, + STARTED, + COLLECTING + } +} diff --git a/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/NoJfrCollector.java b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/NoJfrCollector.java new file mode 100644 index 0000000000000..02a6c88dc0d29 --- /dev/null +++ b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/NoJfrCollector.java @@ -0,0 +1,41 @@ +package io.quarkus.test.junit5.virtual.internal; + +import java.util.List; + +import io.smallrye.common.annotation.SuppressForbidden; +import jdk.jfr.consumer.RecordedEvent; + +public class NoJfrCollector implements Collector { + + @Override + public boolean isRecording() { + return false; + } + + @Override + public void init() { + } + + @Override + public void start() { + } + + @Override + public List stop() { + return List.of(); + } + + @SuppressForbidden(reason = "java.util.logging is authorized here") + @Override + public void shutdown() { + } + + @Override + public void accept(RecordedEvent re) { + } + + @Override + public List getEvents() { + return List.of(); + } +} diff --git a/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtension.java b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtension.java index c7454627ddc34..190b56fda4f9e 100644 --- a/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtension.java +++ b/independent-projects/junit5-virtual-threads/src/main/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtension.java @@ -5,6 +5,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; +import java.util.logging.Logger; import java.util.stream.Collectors; import org.junit.jupiter.api.extension.AfterAllCallback; @@ -20,18 +21,21 @@ import io.quarkus.test.junit5.virtual.ShouldNotPin; import io.quarkus.test.junit5.virtual.ShouldPin; import io.quarkus.test.junit5.virtual.ThreadPinnedEvents; +import io.smallrye.common.annotation.SuppressForbidden; import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedFrame; public class VirtualThreadExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver { + private static final Logger LOGGER = Logger.getLogger(VirtualThreadExtension.class.getName()); + public static final String _COLLECTOR_KEY = "collector"; private ExtensionContext.Namespace namespace; @Override public void beforeAll(ExtensionContext extensionContext) { - Collector collector = new Collector(); + Collector collector = Collector.create(); namespace = ExtensionContext.Namespace.create("loom-unit"); var store = extensionContext.getStore(namespace); store.put(_COLLECTOR_KEY, collector); @@ -104,6 +108,7 @@ private ShouldNotPin getShouldNotPin(Class clazz, Method method) { } @Override + @SuppressForbidden(reason = "java.util.logging is authorized here") public void afterEach(ExtensionContext extensionContext) { Method method = extensionContext.getRequiredTestMethod(); Class clazz = extensionContext.getRequiredTestClass(); @@ -111,7 +116,15 @@ public void afterEach(ExtensionContext extensionContext) { return; } var store = extensionContext.getStore(namespace); - List captured = store.get(_COLLECTOR_KEY, Collector.class).stop(); + Collector collector = store.get(_COLLECTOR_KEY, Collector.class); + + if (!collector.isRecording()) { + LOGGER.warning( + "Recording JFR events is not supported by this VM, @ShouldPin and @ShouldNotPin annotations are not enforced."); + return; + } + + List captured = collector.stop(); List pinEvents = captured.stream() .filter(re -> re.getEventType().getName().equals(CARRIER_PINNED_EVENT_NAME)).collect(Collectors.toList()); diff --git a/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldNotPinTest.java b/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldNotPinTest.java index 3105b2d3eefeb..6a8216f54466b 100644 --- a/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldNotPinTest.java +++ b/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldNotPinTest.java @@ -7,6 +7,7 @@ import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.params.ParameterizedTest; @@ -18,6 +19,7 @@ import io.quarkus.test.junit5.virtual.internal.ignore.LoomUnitExampleShouldNotPinOnSuperClassTest; import io.quarkus.test.junit5.virtual.internal.ignore.LoomUnitExampleShouldPinOnSuperClassTest; +@DisabledIfSystemProperty(named = "java.runtime.name", matches = ".*Semeru.*", disabledReason = "Semeru doesn't support JFR yet") public class ShouldNotPinTest { @ParameterizedTest diff --git a/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldPinTest.java b/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldPinTest.java index 346c5ea5aedb4..42fe4dabbfca3 100644 --- a/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldPinTest.java +++ b/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/ShouldPinTest.java @@ -5,6 +5,7 @@ import java.util.stream.Stream; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.params.ParameterizedTest; @@ -16,6 +17,7 @@ import io.quarkus.test.junit5.virtual.internal.ignore.LoomUnitExampleShouldNotPinOnSuperClassTest; import io.quarkus.test.junit5.virtual.internal.ignore.LoomUnitExampleShouldPinOnSuperClassTest; +@DisabledIfSystemProperty(named = "java.runtime.name", matches = ".*Semeru.*", disabledReason = "Semeru doesn't support JFR yet") public class ShouldPinTest { @ParameterizedTest diff --git a/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtensionTest.java b/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtensionTest.java index 265b88b097c83..2be329d4ad7ca 100644 --- a/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtensionTest.java +++ b/independent-projects/junit5-virtual-threads/src/test/java/io/quarkus/test/junit5/virtual/internal/VirtualThreadExtensionTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import org.junit.jupiter.api.extension.ExecutableInvoker; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.MediaType; @@ -87,6 +88,7 @@ void afterEachShouldNotPinAtMost1() throws NoSuchMethodException { } @Test + @DisabledIfSystemProperty(named = "java.runtime.name", matches = ".*Semeru.*", disabledReason = "Semeru doesn't support JFR yet") void afterEachShouldPinButNoEvents() throws NoSuchMethodException { extensionContext.setMethod(TestClass.class.getDeclaredMethod("methodShouldPinButDoesnt")); assertThatThrownBy(() -> extension.afterEach(extensionContext)) @@ -236,7 +238,7 @@ public Store getStore(StoreScope scope, Namespace namespace) { } } - private static class TestCollector extends Collector { + private static class TestCollector extends JfrCollector { private final List mockEvents; private TestCollector(List mockEvents) { diff --git a/integration-tests/jfr-blocking/pom.xml b/integration-tests/jfr-blocking/pom.xml index 9e9009997ebf6..e03928ff2b081 100644 --- a/integration-tests/jfr-blocking/pom.xml +++ b/integration-tests/jfr-blocking/pom.xml @@ -137,5 +137,18 @@ + + + skip-tests-on-semeru + + + java.vendor + IBM Corporation + + + + true + + diff --git a/integration-tests/jfr-opentelemetry/pom.xml b/integration-tests/jfr-opentelemetry/pom.xml index 8740c2cc352a8..bf77489f5e04b 100644 --- a/integration-tests/jfr-opentelemetry/pom.xml +++ b/integration-tests/jfr-opentelemetry/pom.xml @@ -1,157 +1,171 @@ - - 4.0.0 - - io.quarkus - quarkus-integration-tests-parent - 999-SNAPSHOT - - quarkus-integration-test-jfr-opentelemetry - Quarkus - Integration Tests - JFR OpenTelemetry - - true - - - - io.quarkus - quarkus-rest - - - io.quarkus - quarkus-jfr - - - io.quarkus - quarkus-opentelemetry - - - io.quarkus - quarkus-junit5 - test - - - io.rest-assured - rest-assured - test - - - io.quarkus - quarkus-rest-jackson - - - - io.quarkus - quarkus-jfr-deployment - ${project.version} - pom - test - - - * - * - - - - - io.quarkus - quarkus-opentelemetry-deployment - ${project.version} - pom - test - - - * - * - - - - - io.quarkus - quarkus-rest-deployment - ${project.version} - pom - test - - - * - * - - - - - io.quarkus - quarkus-rest-jackson-deployment - ${project.version} - pom - test - - - * - * - - - - - - - + + 4.0.0 + io.quarkus - quarkus-maven-plugin - - - - build - - - - - - + quarkus-integration-tests-parent + 999-SNAPSHOT + + quarkus-integration-test-jfr-opentelemetry + Quarkus - Integration Tests - JFR OpenTelemetry + + true + + + + io.quarkus + quarkus-rest + + + io.quarkus + quarkus-jfr + + + io.quarkus + quarkus-opentelemetry + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-rest-jackson + + + + io.quarkus + quarkus-jfr-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-opentelemetry-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-rest-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-rest-jackson-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + - - - native-image - - - native - - + + + native-image + + + native + + - - true - + + true + - - - - org.apache.maven.plugins - maven-surefire-plugin - - ${native.surefire.skip} - - + + + + org.apache.maven.plugins + maven-surefire-plugin + + ${native.surefire.skip} + + - - maven-failsafe-plugin - - - - integration-test - verify - - - - - ${project.build.directory}/${project.build.finalName}-runner - - - - - - - - - - + + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + + skip-tests-on-semeru + + + java.vendor + IBM Corporation + + + + true + + + diff --git a/integration-tests/jfr-reactive/pom.xml b/integration-tests/jfr-reactive/pom.xml index 2b17ec29178a2..05a39428fac58 100644 --- a/integration-tests/jfr-reactive/pom.xml +++ b/integration-tests/jfr-reactive/pom.xml @@ -137,5 +137,18 @@ + + + skip-tests-on-semeru + + + java.vendor + IBM Corporation + + + + true + + diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/RegisterForReflectionTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/RegisterForReflectionTestCase.java index bb0042c23e907..aa41dbdb540cf 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/RegisterForReflectionTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/RegisterForReflectionTestCase.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; +import io.quarkus.test.junit.DisabledOnSemeru; import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; @@ -53,6 +54,7 @@ public void testTargetWithoutNested() { } @Test + @DisabledOnSemeru(reason = "The lambda name will be empty on Semeru. As there's little chance we will use Semeru to build a native executable, we can skip this test.") public void testLambdaCapturing() { final String resourceLambda = BASE_PKG + ".ResourceLambda"; diff --git a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java index 41dfa1e8359d3..1e5886628af8c 100644 --- a/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java +++ b/integration-tests/opentelemetry/src/test/java/io/quarkus/it/opentelemetry/MetricsTest.java @@ -5,10 +5,11 @@ import static io.restassured.RestAssured.given; import static java.net.HttpURLConnection.HTTP_OK; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -19,6 +20,7 @@ import org.junit.jupiter.api.Test; import io.opentelemetry.sdk.metrics.data.MetricDataType; +import io.quarkus.test.common.JdkUtil; import io.quarkus.test.junit.QuarkusTest; import io.restassured.common.mapper.TypeRef; @@ -98,18 +100,17 @@ void testAllJvmMetrics() { await().atMost(10, SECONDS).untilAsserted(() -> { Set allMetricNames = getAllMetricNames("jvm."); - assertThat(allMetricNames.size()) - .withFailMessage("The jvm metrics are " + allMetricNames) - .isGreaterThanOrEqualTo(allMetrics.size()); + assertThat(allMetricNames) + .containsAll(allMetrics.stream().map(MetricToAssert::name).toList()); }); allMetrics.forEach(metricToAssert -> { // metric is there and has at least 1 reading await().atMost(10, SECONDS) - .untilAsserted(() -> assertThat(getMetrics(metricToAssert.name()).size()) - .withFailMessage("The metric " + metricToAssert.name()) - .isGreaterThan(0)); + .untilAsserted(() -> assertThat(getMetrics(metricToAssert.name())) + .withFailMessage("The metric " + metricToAssert.name() + " is not defined") + .hasSizeGreaterThan(0)); // skip assertions from flaky metrics if (!metricToAssert.name().equals("jvm.memory.used_after_last_gc") && @@ -164,40 +165,44 @@ record MetricToAssert(String name, String description, String metricUnit, Metric } protected Set getJvmMetricsToAssert() { - return Set.of( - // new MetricToAssert("http.server.request.duration", "Duration of HTTP server requests.", "s", - // HISTOGRAM), // just because we generate load with HTTP - new MetricToAssert("jvm.memory.committed", "Measure of memory committed.", "By", LONG_SUM), - new MetricToAssert("jvm.memory.used", "Measure of memory used.", "By", LONG_SUM), - // Not on native - new MetricToAssert("jvm.memory.limit", "Measure of max obtainable memory.", "By", LONG_SUM), - new MetricToAssert("jvm.memory.used_after_last_gc", - "Measure of memory used, as measured after the most recent garbage collection event on this pool.", - "By", LONG_SUM), - // not on native - new MetricToAssert("jvm.gc.duration", "Duration of JVM garbage collection actions.", "s", - HISTOGRAM), - new MetricToAssert("jvm.class.count", "Number of classes currently loaded.", "{class}", - LONG_SUM), - new MetricToAssert("jvm.class.loaded", "Number of classes loaded since JVM start.", "{class}", - LONG_SUM), - new MetricToAssert("jvm.class.unloaded", "Number of classes unloaded since JVM start.", - "{class}", LONG_SUM), - new MetricToAssert("jvm.cpu.count", - "Number of processors available to the Java virtual machine.", "{cpu}", LONG_SUM), - new MetricToAssert("jvm.cpu.limit", "", "1", LONG_SUM), - //jvm.system.cpu.utilization instead, on native - new MetricToAssert("jvm.cpu.time", "CPU time used by the process as reported by the JVM.", "s", - DOUBLE_SUM), - new MetricToAssert("jvm.cpu.recent_utilization", - "Recent CPU utilization for the process as reported by the JVM.", "1", DOUBLE_GAUGE), - new MetricToAssert("jvm.cpu.longlock", "Long lock times", "s", HISTOGRAM), - new MetricToAssert("jvm.cpu.context_switch", "", "Hz", DOUBLE_SUM), - // not on native - new MetricToAssert("jvm.network.io", "Network read/write bytes.", "By", HISTOGRAM), - new MetricToAssert("jvm.network.time", "Network read/write duration.", "s", HISTOGRAM), - new MetricToAssert("jvm.thread.count", "Number of executing platform threads.", "{thread}", - LONG_SUM)); + Set jvmMetrics = new HashSet<>(); + + // metrics.add(new MetricToAssert("http.server.request.duration", "Duration of HTTP server requests.", "s", HISTOGRAM)); // just because we generate load with HTTP + jvmMetrics.add(new MetricToAssert("jvm.memory.committed", "Measure of memory committed.", "By", LONG_SUM)); + jvmMetrics.add(new MetricToAssert("jvm.memory.used", "Measure of memory used.", "By", LONG_SUM)); + // Not on native + jvmMetrics.add(new MetricToAssert("jvm.memory.limit", "Measure of max obtainable memory.", "By", LONG_SUM)); + jvmMetrics.add(new MetricToAssert("jvm.memory.used_after_last_gc", + "Measure of memory used, as measured after the most recent garbage collection event on this pool.", + "By", LONG_SUM)); + // not on native + jvmMetrics.add(new MetricToAssert("jvm.gc.duration", "Duration of JVM garbage collection actions.", "s", HISTOGRAM)); + jvmMetrics.add(new MetricToAssert("jvm.class.count", "Number of classes currently loaded.", "{class}", LONG_SUM)); + jvmMetrics + .add(new MetricToAssert("jvm.class.loaded", "Number of classes loaded since JVM start.", "{class}", LONG_SUM)); + jvmMetrics.add( + new MetricToAssert("jvm.class.unloaded", "Number of classes unloaded since JVM start.", "{class}", LONG_SUM)); + jvmMetrics.add(new MetricToAssert("jvm.cpu.count", "Number of processors available to the Java virtual machine.", + "{cpu}", LONG_SUM)); + // jvm.system.cpu.utilization instead, on native + jvmMetrics.add( + new MetricToAssert("jvm.cpu.time", "CPU time used by the process as reported by the JVM.", "s", DOUBLE_SUM)); + jvmMetrics.add(new MetricToAssert("jvm.cpu.recent_utilization", + "Recent CPU utilization for the process as reported by the JVM.", "1", DOUBLE_GAUGE)); + jvmMetrics.add(new MetricToAssert("jvm.thread.count", "Number of executing platform threads.", "{thread}", LONG_SUM)); + + // not supported on Semeru + if (!JdkUtil.isSemeru()) { + jvmMetrics.add(new MetricToAssert("jvm.cpu.limit", "", "1", LONG_SUM)); + jvmMetrics.add(new MetricToAssert("jvm.cpu.longlock", "Long lock times", "s", HISTOGRAM)); + jvmMetrics.add(new MetricToAssert("jvm.cpu.context_switch", "", "Hz", DOUBLE_SUM)); + + // not on native + jvmMetrics.add(new MetricToAssert("jvm.network.io", "Network read/write bytes.", "By", HISTOGRAM)); + jvmMetrics.add(new MetricToAssert("jvm.network.time", "Network read/write duration.", "s", HISTOGRAM)); + } + + return jvmMetrics; } private Double getLastReading(MetricToAssert metricToAssert) { diff --git a/test-framework/common/src/main/java/io/quarkus/test/common/JdkUtil.java b/test-framework/common/src/main/java/io/quarkus/test/common/JdkUtil.java new file mode 100644 index 0000000000000..63da16c2a01d7 --- /dev/null +++ b/test-framework/common/src/main/java/io/quarkus/test/common/JdkUtil.java @@ -0,0 +1,10 @@ +package io.quarkus.test.common; + +import java.util.Locale; + +public class JdkUtil { + + public static boolean isSemeru() { + return System.getProperty("java.runtime.name", "").toLowerCase(Locale.ROOT).contains("semeru"); + } +} diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/DisabledOnSemeru.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/DisabledOnSemeru.java new file mode 100644 index 0000000000000..5d7ace03ad397 --- /dev/null +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/DisabledOnSemeru.java @@ -0,0 +1,25 @@ +package io.quarkus.test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Used to signal that a test class or method should be disabled if Semeru is used as the JVM runtime. + *

+ * We cannot test for Semeru exactly but we check the java.vendor is IBM Corporation. + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(DisabledOnSemeruCondition.class) +public @interface DisabledOnSemeru { + + int versionGreaterThanOrEqualTo() default 0; + + int versionLessThanOrEqualTo() default 0; + + String reason() default ""; +} diff --git a/test-framework/junit5-internal/src/main/java/io/quarkus/test/DisabledOnSemeruCondition.java b/test-framework/junit5-internal/src/main/java/io/quarkus/test/DisabledOnSemeruCondition.java new file mode 100644 index 0000000000000..ab4c6bdf2bc76 --- /dev/null +++ b/test-framework/junit5-internal/src/main/java/io/quarkus/test/DisabledOnSemeruCondition.java @@ -0,0 +1,46 @@ +package io.quarkus.test; + +import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation; + +import java.lang.reflect.AnnotatedElement; +import java.util.Optional; + +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; + +import io.quarkus.test.common.JdkUtil; + +public class DisabledOnSemeruCondition implements ExecutionCondition { + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + Optional element = context.getElement(); + Optional optional = findAnnotation(element, DisabledOnSemeru.class); + if (optional.isEmpty()) { + return ConditionEvaluationResult.enabled("@DisabledOnSemeru was not found"); + } + if (!JdkUtil.isSemeru()) { + return ConditionEvaluationResult.enabled("JVM is not identified as Semeru"); + } + + DisabledOnSemeru disabledOnSemeru = optional.get(); + if (disabledOnSemeru.versionGreaterThanOrEqualTo() > 0 || disabledOnSemeru.versionLessThanOrEqualTo() > 0) { + if (disabledOnSemeru.versionGreaterThanOrEqualTo() > 0) { + if (Runtime.version().feature() < disabledOnSemeru.versionGreaterThanOrEqualTo()) { + return ConditionEvaluationResult.disabled( + "JVM identified as Semeru and JVM version < " + disabledOnSemeru.versionGreaterThanOrEqualTo()); + } + } + if (disabledOnSemeru.versionLessThanOrEqualTo() > 0) { + if (Runtime.version().feature() > disabledOnSemeru.versionLessThanOrEqualTo()) { + return ConditionEvaluationResult.disabled( + "JVM identified as Semeru and JVM version > " + disabledOnSemeru.versionLessThanOrEqualTo()); + } + } + return ConditionEvaluationResult.enabled("JVM is identified as Semeru but version matches"); + } + + return ConditionEvaluationResult.disabled("JVM is identified as Semeru"); + } +} diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/DisabledOnSemeru.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/DisabledOnSemeru.java new file mode 100644 index 0000000000000..4296e30474bd6 --- /dev/null +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/DisabledOnSemeru.java @@ -0,0 +1,25 @@ +package io.quarkus.test.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Used to signal that a test class or method should be disabled if Semeru is used as the JVM runtime. + *

+ * We cannot test for Semeru exactly but we check the java.vendor is IBM Corporation. + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(DisabledOnSemeruCondition.class) +public @interface DisabledOnSemeru { + + int versionGreaterThanOrEqualTo() default 0; + + int versionLessThanOrEqualTo() default 0; + + String reason() default ""; +} diff --git a/test-framework/junit5/src/main/java/io/quarkus/test/junit/DisabledOnSemeruCondition.java b/test-framework/junit5/src/main/java/io/quarkus/test/junit/DisabledOnSemeruCondition.java new file mode 100644 index 0000000000000..46f114a7ce725 --- /dev/null +++ b/test-framework/junit5/src/main/java/io/quarkus/test/junit/DisabledOnSemeruCondition.java @@ -0,0 +1,46 @@ +package io.quarkus.test.junit; + +import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation; + +import java.lang.reflect.AnnotatedElement; +import java.util.Optional; + +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; + +import io.quarkus.test.common.JdkUtil; + +public class DisabledOnSemeruCondition implements ExecutionCondition { + + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { + Optional element = context.getElement(); + Optional optional = findAnnotation(element, DisabledOnSemeru.class); + if (optional.isEmpty()) { + return ConditionEvaluationResult.enabled("@DisabledOnSemeru was not found"); + } + if (!JdkUtil.isSemeru()) { + return ConditionEvaluationResult.enabled("JVM is not identified as Semeru"); + } + + DisabledOnSemeru disabledOnSemeru = optional.get(); + if (disabledOnSemeru.versionGreaterThanOrEqualTo() > 0 || disabledOnSemeru.versionLessThanOrEqualTo() > 0) { + if (disabledOnSemeru.versionGreaterThanOrEqualTo() > 0) { + if (Runtime.version().feature() < disabledOnSemeru.versionGreaterThanOrEqualTo()) { + return ConditionEvaluationResult.disabled( + "JVM identified as Semeru and JVM version < " + disabledOnSemeru.versionGreaterThanOrEqualTo()); + } + } + if (disabledOnSemeru.versionLessThanOrEqualTo() > 0) { + if (Runtime.version().feature() > disabledOnSemeru.versionLessThanOrEqualTo()) { + return ConditionEvaluationResult.disabled( + "JVM identified as Semeru and JVM version > " + disabledOnSemeru.versionLessThanOrEqualTo()); + } + } + return ConditionEvaluationResult.enabled("JVM is identified as Semeru but version matches"); + } + + return ConditionEvaluationResult.disabled("JVM is identified as Semeru"); + } +}