diff --git a/.github/workflows/local_tests.yml b/.github/workflows/local_tests.yml
index 88e1da8b..ec22ea48 100644
--- a/.github/workflows/local_tests.yml
+++ b/.github/workflows/local_tests.yml
@@ -139,7 +139,7 @@ jobs:
)
pushd ts
copy .github/mvn-settings.xml ~/.m2/settings.xml
- mvn clean verify -Ptestsuite -Dquarkus.version=${{ matrix.quarkus-version }}
+ mvn clean verify -Ptestsuite -DincludeTags=testing-testsuite,runtimes,reproducers -Dquarkus.version=${{ matrix.quarkus-version }}
shell: cmd
- name: Linux test
if: startsWith(matrix.os, 'ubuntu')
@@ -159,7 +159,7 @@ jobs:
native-image --version
pushd ts
cp .github/mvn-settings.xml ~/.m2/settings.xml
- mvn clean verify -Ptestsuite -Dquarkus.version=${{ matrix.quarkus-version }}
+ mvn clean verify -Ptestsuite -DincludeTags=testing-testsuite,runtimes,reproducers -Dquarkus.version=${{ matrix.quarkus-version }}
shell: bash
- name: Prepare failure archive (if maven failed)
if: failure()
diff --git a/testsuite/pom.xml b/testsuite/pom.xml
index 46538976..f0b0bb9e 100644
--- a/testsuite/pom.xml
+++ b/testsuite/pom.xml
@@ -56,7 +56,7 @@
org.graalvm.sdk
graal-sdk
- 21.2.0
+ 21.3.1
diff --git a/testsuite/src/it/java/org/graalvm/tests/integration/AppReproducersTest.java b/testsuite/src/it/java/org/graalvm/tests/integration/AppReproducersTest.java
index 390bd8b1..71a493c6 100644
--- a/testsuite/src/it/java/org/graalvm/tests/integration/AppReproducersTest.java
+++ b/testsuite/src/it/java/org/graalvm/tests/integration/AppReproducersTest.java
@@ -30,9 +30,7 @@
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
-import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.DisabledOnOs;
-import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.api.condition.OS;
import java.io.File;
@@ -572,8 +570,7 @@ public void timezonesBakedIn(TestInfo testInfo) throws IOException, InterruptedE
@Test
@Tag("jdk-17")
@Tag("recordannotations")
- @DisabledOnJre(JRE.JAVA_11)
- @IfMandrelVersion(min = "21.3.1.1")
+ @IfMandrelVersion(min = "21.3.1.1", minJDK = "17")
public void recordAnnotationsWork(TestInfo testInfo) throws IOException, InterruptedException {
final Apps app = Apps.RECORDANNOTATIONS;
LOGGER.info("Testing app: " + app);
diff --git a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/IfMandrelVersion.java b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/IfMandrelVersion.java
index 71c9c0a1..f85b5af5 100644
--- a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/IfMandrelVersion.java
+++ b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/IfMandrelVersion.java
@@ -40,6 +40,13 @@
*
* IfMandrelVersion(min = "21.1", inContainer = true)
* i.e. [21.1, +∞)
+ *
+ * IfMandrelVersion(min = "21.3.1", minJDK = "17")
+ * i.e. [21.3.1, +∞) and [17, +∞)
+ *
+ * IfMandrelVersion(min = "22", minJDK = "17", maxJDK = "17.0.2")
+ * i.e. [22, +∞) and [17, 17.0.2]
+ *
* //@formatter:on
* Note that versions 21.1.0.0-final and 21.1.0.0-snapshot and 21.1.0.0 are all considered equal.
*
@@ -47,6 +54,11 @@
*
* inContainer: Whether the version should be pulled from a builder image container.
*
+ * JDK versions comparator uses feature.interim.update, the fourth, patch number or
+ * any other qualifiers following that are not used, i.e.
+ * 17.0.3-beta+5-202203292328 and 17.0.3-beta+6 are the same and
+ * 11.0.14.1+1-LTS and 11.0.14 are the same.
+ *
* @author Michal Karm Babacek
*/
@Target(ElementType.METHOD)
@@ -58,5 +70,9 @@
String max() default "";
+ String minJDK() default "";
+
+ String maxJDK() default "";
+
boolean inContainer() default false;
}
diff --git a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/MandrelVersionCondition.java b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/MandrelVersionCondition.java
index 4c4bf485..a3b4e9cb 100644
--- a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/MandrelVersionCondition.java
+++ b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/MandrelVersionCondition.java
@@ -25,8 +25,11 @@
import org.junit.jupiter.api.extension.ExtensionContext;
import java.lang.reflect.AnnotatedElement;
+import java.util.regex.Pattern;
import static java.lang.String.format;
+import static org.graalvm.tests.integration.utils.versions.UsedVersion.compareJDKVersion;
+import static org.graalvm.tests.integration.utils.versions.UsedVersion.featureInterimUpdate;
import static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;
import static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;
import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;
@@ -52,15 +55,28 @@ public ConditionEvaluationResult evaluateExecutionCondition(
private ConditionEvaluationResult disableIfVersionMismatch(IfMandrelVersion annotation, AnnotatedElement element) {
final Version usedVersion = UsedVersion.getVersion(annotation.inContainer());
- if (annotation.min().isBlank() || usedVersion.compareTo(Version.parse(annotation.min())) >= 0) {
- if (annotation.max().isBlank() || usedVersion.compareTo(Version.parse(annotation.max())) <= 0) {
- return enabled(format(
- "%s is enabled as Mandrel version %s does satisfy constraints: minVersion: %s, maxVersion: %s",
- element, usedVersion.toString(), annotation.min(), annotation.max()));
- }
+ boolean jdkConstraintSatisfied = true;
+ if (!annotation.minJDK().isBlank() || !annotation.maxJDK().isBlank()) {
+ final int[] jdkVersion = new int[]{
+ UsedVersion.jdkFeature(annotation.inContainer()),
+ UsedVersion.jdkInterim(annotation.inContainer()),
+ UsedVersion.jdkUpdate(annotation.inContainer())
+ };
+ final Pattern p = Pattern.compile("(?[0-9]+)(\\.(?[0-9]*)\\.(?[0-9]*))?");
+ final int[] min = featureInterimUpdate(p, annotation.minJDK(), Integer.MIN_VALUE);
+ final int[] max = featureInterimUpdate(p, annotation.maxJDK(), Integer.MAX_VALUE);
+ jdkConstraintSatisfied = compareJDKVersion(jdkVersion, min) >= 0 && compareJDKVersion(jdkVersion, max) <= 0;
+ }
+ final boolean mandrelConstraintSatisfied =
+ (annotation.min().isBlank() || usedVersion.compareTo(Version.parse(annotation.min())) >= 0) &&
+ (annotation.max().isBlank() || usedVersion.compareTo(Version.parse(annotation.max())) <= 0);
+ if (mandrelConstraintSatisfied && jdkConstraintSatisfied) {
+ return enabled(format(
+ "%s is enabled as Mandrel version %s does satisfy constraints: min: %s, max: %s, minJDK: %s, maxJDK: %s",
+ element, usedVersion.toString(), annotation.min(), annotation.max(), annotation.minJDK(), annotation.maxJDK()));
}
return disabled(format(
- "%s is disabled as Mandrel version %s does not satisfy constraints: minVersion: %s, maxVersion: %s",
- element, usedVersion, annotation.min(), annotation.max()));
+ "%s is disabled as Mandrel version %s does not satisfy constraints: min: %s, max: %s, minJDK: %s, maxJDK: %s",
+ element, usedVersion, annotation.min(), annotation.max(), annotation.minJDK(), annotation.maxJDK()));
}
}
diff --git a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/QuarkusVersion.java b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/QuarkusVersion.java
index 4921a517..124677bc 100644
--- a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/QuarkusVersion.java
+++ b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/QuarkusVersion.java
@@ -25,7 +25,7 @@ public class QuarkusVersion implements Comparable {
private final int major;
private final int minor;
private final int patch;
- private boolean snapshot;
+ private final boolean snapshot;
public QuarkusVersion(String version) {
this.version = version;
diff --git a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/UsedVersion.java b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/UsedVersion.java
index 2dc1f3be..a3d5e1b0 100644
--- a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/UsedVersion.java
+++ b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/UsedVersion.java
@@ -49,6 +49,7 @@
* @author Michal Karm Babacek
*/
public class UsedVersion {
+ private static final int UNDEFINED = -1;
public static Version getVersion(boolean inContainer) {
return inContainer ? InContainer.mVersion.version : Locally.mVersion.version;
@@ -75,8 +76,8 @@ public static int jdkUpdate(boolean inContainer) {
*/
private static final class MVersion {
private static final Logger LOGGER = Logger.getLogger(MVersion.class.getName());
- private static final Pattern VERSION_PATTERN =
- Pattern.compile("(?:GraalVM|native-image)(?: Version)? ([^ ]*).*Java Version ([\\d]+)\\.([\\d]+)\\.([\\d]+).*");
+ private static final Pattern VERSION_PATTERN = Pattern.compile(
+ "(?:GraalVM|native-image)(?: Version)? (?[^ ]*).*Java Version (?[\\d]+)\\.(?[\\d]+)\\.(?[\\d]+).*");
private final Version version;
private final boolean jdkUsesSysLibs;
@@ -98,7 +99,8 @@ public MVersion(boolean inContainer) {
final String[] lines = out.split(System.lineSeparator());
lastLine = lines[lines.length - 1].trim();
} else {
- final List cmd = getRunCommand("native-image", "--version");
+ final String TEST_TESTSUITE_ABSOLUTE_PATH = System.getProperty("FAKE_NATIVE_IMAGE_DIR", "");
+ final List cmd = getRunCommand(TEST_TESTSUITE_ABSOLUTE_PATH + "native-image", "--version");
LOGGER.info("Running command " + cmd + " to determine Mandrel version used.");
try {
lastLine = Commands.runCommand(cmd).trim();
@@ -124,10 +126,17 @@ public MVersion(boolean inContainer) {
"Output: '" + lastLine + "'");
}
}
- version = versionParse(m.group(1));
- jdkFeature = Integer.parseInt(m.group(2));
- jdkInterim = Integer.parseInt(m.group(3));
- jdkUpdate = Integer.parseInt(m.group(4));
+ version = versionParse(m.group("version"));
+ final String jFeature = m.group("jfeature");
+ final String jInterim = m.group("jinterim");
+ final String jUpdate = m.group("jupdate");
+ jdkFeature = jFeature == null ? UNDEFINED : Integer.parseInt(jFeature);
+ jdkInterim = jInterim == null ? UNDEFINED : Integer.parseInt(jInterim);
+ jdkUpdate = jUpdate == null ? UNDEFINED : Integer.parseInt(jUpdate);
+ if (jdkFeature == UNDEFINED) {
+ LOGGER.warn("Failed to correctly parse Java feature (major) version from native-image version command output. " +
+ "JDK version constraints in tests won't work reliably.");
+ }
LOGGER.infof("The test suite runs with Mandrel version %s %s, JDK %d.%d.%d.",
version.toString(), inContainer ? "in container" : " installed locally on PATH", jdkFeature, jdkInterim, jdkUpdate);
}
@@ -149,4 +158,31 @@ private static class Locally {
private static final MVersion mVersion = new MVersion(false);
}
+ public static int[] featureInterimUpdate(Pattern pattern, String version, int defaultValue) {
+ final Matcher m = pattern.matcher(version);
+ if (!m.matches()) {
+ return new int[]{defaultValue, defaultValue, defaultValue};
+ }
+ final String jFeature = m.group("jfeature");
+ final String jInterim = m.group("jinterim");
+ final String jUpdate = m.group("jupdate");
+ return new int[]{
+ jFeature == null ? defaultValue : Integer.parseInt(jFeature),
+ jInterim == null ? defaultValue : Integer.parseInt(jInterim),
+ jUpdate == null ? defaultValue : Integer.parseInt(jUpdate)
+ };
+ }
+
+ public static int compareJDKVersion(int[] a, int[] b) {
+ if (a.length != 3 || b.length != 3) {
+ throw new IllegalArgumentException("3 version elements expected: feature, interim, update");
+ }
+ for (int i = 0; i < 3; i++) {
+ int compare = Integer.compare(a[i], b[i]);
+ if (compare != 0) {
+ return compare;
+ }
+ }
+ return 0;
+ }
}
diff --git a/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/VersionsTest.java b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/VersionsTest.java
new file mode 100644
index 00000000..863c27fa
--- /dev/null
+++ b/testsuite/src/it/java/org/graalvm/tests/integration/utils/versions/VersionsTest.java
@@ -0,0 +1,154 @@
+package org.graalvm.tests.integration.utils.versions;/*
+ * Copyright (c) 2022, Red Hat Inc. All rights reserved.
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import org.graalvm.tests.integration.utils.Logs;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.PosixFilePermissions;
+
+import static org.graalvm.tests.integration.utils.Commands.IS_THIS_WINDOWS;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Testing test suite...
+ *
+ * A fake native-image executable script is created, it contains a single version string.
+ * Using the property FAKE_NATIVE_IMAGE_DIR, the test suite is tricked into running it
+ * during native-image version resolution.
+ * A series of test methods is executed by JUnit, business as usual. Those test methods log
+ * their executing into a file.
+ * After all tests are done, the file is examined. If it contains any superfluous entries
+ * or if it's missing anything, the test lass fails.
+ */
+@Tag("testing-testsuite")
+public class VersionsTest {
+ static final Path NATIVE_IMAGE = Path.of(System.getProperty("java.io.tmpdir"), IS_THIS_WINDOWS ? "native-image.cmd" : "native-image");
+ static final String VERSION = "native-image 22.2.0-devdb26f5c4fbe Mandrel Distribution (Java Version 17.0.3-beta+5-202203292328)";
+ static final Path LOG = Path.of(System.getProperty("java.io.tmpdir"), "versions-log");
+ static final StandardOpenOption[] LOG_FILE_OPS = new StandardOpenOption[]{StandardOpenOption.CREATE, StandardOpenOption.APPEND, StandardOpenOption.WRITE};
+
+ @BeforeAll
+ public static void setup() throws IOException {
+ System.setProperty("FAKE_NATIVE_IMAGE_DIR", System.getProperty("java.io.tmpdir") + File.separator);
+ Files.writeString(NATIVE_IMAGE, IS_THIS_WINDOWS ?
+ "@echo off" + System.lineSeparator() +
+ "echo " + VERSION + System.lineSeparator() :
+ "#!/bin/sh" + System.lineSeparator() +
+ "echo '" + VERSION + "'" + System.lineSeparator(),
+ StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
+ if(!IS_THIS_WINDOWS) {
+ Files.setPosixFilePermissions(NATIVE_IMAGE, PosixFilePermissions.fromString("rwxr-xr-x"));
+ }
+ Files.deleteIfExists(LOG);
+ }
+
+ @AfterAll
+ public static void teardown() throws IOException {
+ System.clearProperty("FAKE_NATIVE_IMAGE_DIR");
+ Files.deleteIfExists(NATIVE_IMAGE);
+ final String testlog = Files.readString(LOG, StandardCharsets.UTF_8);
+ try {
+ assertEquals(
+ "Running test jdkVersionCheckA\n" +
+ "Running test jdkVersionCheckB\n" +
+ "Running test jdkVersionCheckC\n" +
+ "Running test jdkVersionCheckD\n" +
+ "Running test jdkVersionCheckI\n", testlog);
+ } finally {
+ Logs.archiveLog(VersionsTest.class.getCanonicalName(), "versionTest", LOG.toFile());
+ Files.deleteIfExists(LOG);
+ }
+ }
+
+ @Test
+ @Order(1)
+ @IfMandrelVersion(min = "21.3.1")
+ public void jdkVersionCheckA(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(2)
+ @IfMandrelVersion(min = "21.3.1", minJDK = "17")
+ public void jdkVersionCheckB(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(3)
+ @IfMandrelVersion(min = "22", minJDK = "17")
+ public void jdkVersionCheckC(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(4)
+ @IfMandrelVersion(min = "22", minJDK = "17.0.2")
+ public void jdkVersionCheckD(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(5)
+ @IfMandrelVersion(min = "22", minJDK = "17", maxJDK = "17.0.2")
+ public void jdkVersionCheckE(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(6)
+ @IfMandrelVersion(min = "21", minJDK = "11.0.12", maxJDK = "17.0.1")
+ public void jdkVersionCheckF(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(7)
+ @IfMandrelVersion(min = "21", maxJDK = "11")
+ public void jdkVersionCheckG(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(8)
+ @IfMandrelVersion(min = "21.2", minJDK = "18.0.0")
+ public void jdkVersionCheckH(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+ @Test
+ @Order(9)
+ @IfMandrelVersion(min = "22", max = "22.2", minJDK = "17.0.1", maxJDK = "17.0.3")
+ public void jdkVersionCheckI(TestInfo testInfo) throws IOException {
+ Files.writeString(LOG, "Running test " + testInfo.getTestMethod().get().getName() + "\n", StandardCharsets.UTF_8, LOG_FILE_OPS);
+ }
+
+}