This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 71055ec948 GROOVY-11788: Bump JUnit to 6.0.3
71055ec948 is described below

commit 71055ec948620ac2965477b586123c03682f8372
Author: Paul King <[email protected]>
AuthorDate: Sat Mar 28 22:34:58 2026 +1000

    GROOVY-11788: Bump JUnit to 6.0.3
---
 .../groovy/org.apache.groovy-documented.gradle     |   2 +-
 .../groovy/org.apache.groovy-performance.gradle    |   6 +-
 .../main/groovy/org.apache.groovy-tested.gradle    |   6 +-
 build.gradle                                       |   2 +-
 gradle/verification-metadata.xml                   |  42 +++++++
 settings.gradle                                    |   1 +
 subprojects/groovy-test-junit5/build.gradle        |   5 +-
 .../build.gradle                                   |  13 +-
 .../junit6/plugin/GroovyJUnitRunnerHelper.groovy   |  49 ++++++++
 .../java/groovy/junit6/plugin/JUnit6Runner.java    | 136 +++++++++++++++++++++
 .../java/groovy/junit6/plugin/package-info.java    |  23 ++++
 .../services/org.apache.groovy.plugin.GroovyRunner |  20 +++
 .../src/test/groovy/JUnit6Test.groovy              |  60 +++++++++
 versions.properties                                |   1 +
 14 files changed, 349 insertions(+), 17 deletions(-)

diff --git a/build-logic/src/main/groovy/org.apache.groovy-documented.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
index 9b191e0b54..789002e7e6 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-documented.gradle
@@ -84,7 +84,7 @@ tasks.withType(Groovydoc).configureEach {
     link "https://docs.oracle.com/javase/${17}/docs/api/java.base/";, 'java.', 
'org.xml.', 'javax.', 'org.w3c.'
     link 'https://docs.groovy-lang.org/docs/ant/api/', 'org.apache.ant.', 
'org.apache.tools.ant.'
     link "https://junit.org/junit4/javadoc/${versions.junit}/";, 'org.junit.', 
'junit.'
-    link 
"https://docs.junit.org/${versions.junit5}/api/org.junit.jupiter.api/";, 
'org.junit.jupiter.', 'org.junit.platform.'
+    link "https://docs.junit.org/${versions.junit6}/api/";, 
'org.junit.jupiter.', 'org.junit.platform.'
     link 'https://www.antlr.org/api/Java/', 'org.antlr.v4.'
     link 
'https://commons.apache.org/proper/commons-cli/javadocs/api-release/', 
'org.apache.commons.cli.'
     link 'https://picocli.info/apidocs/', 'picocli.'
diff --git a/build-logic/src/main/groovy/org.apache.groovy-performance.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-performance.gradle
index a4405f825f..f8555a2a9d 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-performance.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-performance.gradle
@@ -44,9 +44,9 @@ repositories {
 dependencies {
     jmh project(':')
     testImplementation project(':')
-    testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit5}"
-    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.junit5}"
-    testRuntimeOnly 
"org.junit.platform:junit-platform-launcher:${versions.junit5Platform}"
+    testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit6}"
+    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${versions.junit6}"
+    testRuntimeOnly 
"org.junit.platform:junit-platform-launcher:${versions.junit6}"
 
     stats "org.apache.commons:commons-math3:${versions.commonsMath3}"
 }
diff --git a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle 
b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
index 4f588f9ea1..4d5c30ba89 100644
--- a/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
+++ b/build-logic/src/main/groovy/org.apache.groovy-tested.gradle
@@ -35,9 +35,9 @@ sourceSets {
 
 dependencies {
     testCompileOnly "junit:junit:${versions.junit}"
-    testImplementation "org.junit.jupiter:junit-jupiter:${versions.junit5}"
-    testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${versions.junit5}"
-    testRuntimeOnly 
"org.junit.platform:junit-platform-launcher:${versions.junit5Platform}", {
+    testImplementation "org.junit.jupiter:junit-jupiter:${versions.junit6}"
+    testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${versions.junit6}"
+    testRuntimeOnly 
"org.junit.platform:junit-platform-launcher:${versions.junit6}", {
         exclude group: 'org.apiguardian', module: 'apiguardian-api'
     }
 }
diff --git a/build.gradle b/build.gradle
index ef6c4db67a..0143bc0880 100644
--- a/build.gradle
+++ b/build.gradle
@@ -122,7 +122,7 @@ dependencies {
 
     testFixturesImplementation projects.groovyXml
     testFixturesImplementation projects.groovyTest
-    testFixturesImplementation 
"org.junit.jupiter:junit-jupiter-api:${versions.junit5}"
+    testFixturesImplementation 
"org.junit.jupiter:junit-jupiter-api:${versions.junit6}"
     testFixturesImplementation "xmlunit:xmlunit:${versions.xmlunit}"
 
     tools "com.eed3si9n.jarjar:ant-jarjar:${versions.jarjar}"
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 35e6d909ba..48077fa700 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -220,6 +220,7 @@
             <trusting group="org.apache.commons"/>
          </trusted-key>
          <trusted-key id="BDB5FA4FE719D787FB3D3197F6D4A1D411E9D1AE" 
group="com.google.guava"/>
+         <trusted-key id="BED597EFC4BF399D3283F697432DC97E7B73F0B6" 
group="org.apache.ant"/>
          <trusted-key id="C4F54D8622C95CC3F098721A0F13D5631D6AF36D" 
group="org.testng" name="testng" version="7.10.2"/>
          <trusted-key id="C668130C58722C9DC1C0FC633F1EB974E7AB9B37" 
group="biz.aQute.bnd"/>
          <trusted-key id="C7BE5BCC9FEC15518CFDA882B0F3710FA64900E7" 
group="com.google.code.gson" name="gson"/>
@@ -2267,36 +2268,71 @@
             <sha512 
value="ec8c3f06c181ce7bc2803d7769701645b40f05d9d4ac948f9de6ed2ebaf76bba727d2b91d4d81defce082ac30215e29ede59028e5fd8d86bd5df1bbc5b7cea23"
 origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.junit.jupiter" name="junit-jupiter" 
version="6.0.3">
+         <artifact name="junit-jupiter-6.0.3.jar">
+            <sha512 
value="a7e4e6ac16bb0efec4fe36941e61835c8e28c2de535bba6f1e69b84781eb3186fa7cb9b63ee4499f9dc42d332c271d50c8c27e9b1db77aca43ad00f2de4a5dc1"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.junit.jupiter" name="junit-jupiter-api" 
version="5.14.3">
          <artifact name="junit-jupiter-api-5.14.3.jar">
             <sha512 
value="5ada794d305920ceecc00ccd02c48f54bf3d4b36002810f543a2215eff5034519ba939c2451f42828ea4d0a19712cf1e9ec23dd0e91a6fe568bbd7ee023253d8"
 origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.junit.jupiter" name="junit-jupiter-api" 
version="6.0.3">
+         <artifact name="junit-jupiter-api-6.0.3.jar">
+            <sha512 
value="145605177009d17d0d5698f7d827db805b5ea53fc54e67058cb1d8fdd79123a85da6f5e1a28fd8105d08175b74c5dd3a99928aa207ab00d734a1febfc70db1fb"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.junit.jupiter" name="junit-jupiter-engine" 
version="5.14.3">
          <artifact name="junit-jupiter-engine-5.14.3.jar">
             <sha512 
value="73de20444b8898e8fdbea282055807ac280676606e0bfabd25375e580cb5bcce163e9c3fa15828cd7600d364cd0bbac92554560cebed9e7e5116a3693b3fc074"
 origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.junit.jupiter" name="junit-jupiter-engine" 
version="6.0.3">
+         <artifact name="junit-jupiter-engine-6.0.3.jar">
+            <sha512 
value="8d9b04a779d23c1fe76daf446de804dccca785d5f5f55df5d3b3aa373694a2c210e142b0e27d0ae56de28316bfd6c8cd4ac6bb93094b3cf959a867c4e333d226"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.junit.jupiter" name="junit-jupiter-params" 
version="5.14.3">
          <artifact name="junit-jupiter-params-5.14.3.jar">
             <sha512 
value="00367c3b8c02fbe91c07bb889763777eda02a25a88ed9fb8013d00d6c2f9171ea85e4af7a4326859485aa342600c3d90be5be2883f3953280ff184bb7085465a"
 origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.junit.jupiter" name="junit-jupiter-params" 
version="6.0.3">
+         <artifact name="junit-jupiter-params-6.0.3.jar">
+            <sha512 
value="bd1215b879f8cb63162c9a219550acf3bbaa143a8161b276812f124af784112a2fd94b6bae983beafd0de7f6d0c36f223727bb41b7d6d87947500b750fffdede"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.junit.platform" name="junit-platform-commons" 
version="1.14.3">
          <artifact name="junit-platform-commons-1.14.3.jar">
             <sha512 
value="dd9fea9acbd864594c9f750822dcb17d43f6ad37b68bb4a8729f1813d612c177f27efc6b25d983e5195be8aad3482052685b2b9698205730cff35b585114343a"
 origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.junit.platform" name="junit-platform-commons" 
version="6.0.3">
+         <artifact name="junit-platform-commons-6.0.3.jar">
+            <sha512 
value="a22154edbf3c277421dc87e924f5cbe7e2807df43a7b14911303d9f578b4cbe1f3dd5123aff2b94cecd5def3a5141710d6c127f58a03f6ace30be5bd3171fd6e"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.junit.platform" name="junit-platform-engine" 
version="1.14.3">
          <artifact name="junit-platform-engine-1.14.3.jar">
             <sha512 
value="51a681bb853236b618e42c9c7922a035de515bfa217e3d5f1fa917486d0398c0ffb1cc5504a16130ba06976c24a76d213e8676231f1badb19fc2a164c80b92df"
 origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.junit.platform" name="junit-platform-engine" 
version="6.0.3">
+         <artifact name="junit-platform-engine-6.0.3.jar">
+            <sha512 
value="0235da442ee6e189a87f06415c97fdbae1146f5a18ba2e0e9fc57a86916fe4ca1ccf883bb8b96730a524179217de88638cdf87ce7c8ba284cd0fe6f79d93f7aa"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.junit.platform" name="junit-platform-launcher" 
version="1.14.3">
          <artifact name="junit-platform-launcher-1.14.3.jar">
             <sha512 
value="0437a261bfa2c7fc8fcf9b134515073683435db1e810616cd1f45f4bd89cd8c1c7ce98f19c8896d4c3d737fc0f5536ab69e1fdc207fc019ff4f11f10764ec664"
 origin="Generated by Gradle"/>
          </artifact>
       </component>
+      <component group="org.junit.platform" name="junit-platform-launcher" 
version="6.0.3">
+         <artifact name="junit-platform-launcher-6.0.3.jar">
+            <sha512 
value="f368b25eb2394328994fe587e8dc295e9e677c55c8d7b559d5798a262e22bbe963cdfffdc0879b54acc2ae500a78e17a7b80e0638d5d411a8b6c84fb11fbe4e3"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.junit.platform" name="junit-platform-runner" 
version="1.14.3">
          <artifact name="junit-platform-runner-1.14.3.jar">
             <sha512 
value="24c808b481faea635868cc6877ebd342e1b9bd06616d3227d41caf26a12bf9042d410fd27f29a25781a35d6d5594eff4748aec40ae610bfe35b842a10269534f"
 origin="Generated by Gradle"/>
@@ -2318,6 +2354,12 @@
             <sha512 
value="70fac02e1de4f655181fd3505ad61968e971edf59d9eff0b26a657dbdd67ebfcf43f6988cf751d7fb6c443d4caa8293ba65bc567f25129eac2fb6e8ed2b293e3"
 origin="Generated by Gradle" reason="Artifact is not signed"/>
          </artifact>
       </component>
+      <component group="org.junit.vintage" name="junit-vintage-engine" 
version="6.0.3">
+         <artifact name="junit-vintage-engine-6.0.3.jar">
+            <pgp value="FF6E2C001948C5F2F38B0CC385911F425EC61B51"/>
+            <sha512 
value="7b0b92cf0a2bfacfd63417288b5dd115b7ddb2f6c128deb7d5045021e86ca0ce89267b56b70048acd0f600d1137350c485f35981cf57ac991755ee66019f4441"
 origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="org.jvnet.staxex" name="stax-ex" version="1.8.1">
          <artifact name="stax-ex-1.8.1.jar">
             <pgp value="04543577D6A9CC626239C50C7ECBD740FF06AEB5"/>
diff --git a/settings.gradle b/settings.gradle
index 908f53e871..07f1735d75 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -75,6 +75,7 @@ def subprojects = [
         'groovy-templates',
         'groovy-test',
         'groovy-test-junit5',
+        'groovy-test-junit6',
         'groovy-testng',
         'groovy-toml',
         'groovy-typecheckers',
diff --git a/subprojects/groovy-test-junit5/build.gradle 
b/subprojects/groovy-test-junit5/build.gradle
index 5bdc1c3e16..b3d17c1e50 100644
--- a/subprojects/groovy-test-junit5/build.gradle
+++ b/subprojects/groovy-test-junit5/build.gradle
@@ -21,6 +21,10 @@ plugins {
     id 'org.apache.groovy-library'
 }
 
+groovyLibrary {
+    optionalModule()
+}
+
 dependencies {
     api rootProject // JUnit5Runner implements GroovyRunner...
     api "org.junit.jupiter:junit-jupiter-api:${versions.junit5}", {
@@ -37,5 +41,4 @@ dependencies {
     testImplementation "net.jqwik:jqwik-api:${versions.jqwik}"
     testRuntimeOnly    "net.jqwik:jqwik-engine:${versions.jqwik}"
     testImplementation 
"org.junit.jupiter:junit-jupiter-params:${versions.junit5}"
-    testRuntimeOnly    
"org.junit.platform:junit-platform-runner:${versions.junit5Platform}"
 }
diff --git a/subprojects/groovy-test-junit5/build.gradle 
b/subprojects/groovy-test-junit6/build.gradle
similarity index 77%
copy from subprojects/groovy-test-junit5/build.gradle
copy to subprojects/groovy-test-junit6/build.gradle
index 5bdc1c3e16..6f1c4a3183 100644
--- a/subprojects/groovy-test-junit5/build.gradle
+++ b/subprojects/groovy-test-junit6/build.gradle
@@ -22,20 +22,17 @@ plugins {
 }
 
 dependencies {
-    api rootProject // JUnit5Runner implements GroovyRunner...
-    api "org.junit.jupiter:junit-jupiter-api:${versions.junit5}", {
+    api rootProject // JUnit6Runner implements GroovyRunner...
+    api "org.junit.jupiter:junit-jupiter-api:${versions.junit6}", {
         exclude group: 'org.apiguardian', module: 'apiguardian-api'
         exclude group: 'org.junit.platform', module: 'junit-platform-commons'
     }
-    implementation 
"org.junit.jupiter:junit-jupiter-engine:${versions.junit5}", {
+    implementation 
"org.junit.jupiter:junit-jupiter-engine:${versions.junit6}", {
         exclude group: 'org.apiguardian', module: 'apiguardian-api'
     }
-    implementation 
"org.junit.platform:junit-platform-launcher:${versions.junit5Platform}", {
+    implementation 
"org.junit.platform:junit-platform-launcher:${versions.junit6}", {
         exclude group: 'org.apiguardian', module: 'apiguardian-api'
     }
     testImplementation projects.groovyTest
-    testImplementation "net.jqwik:jqwik-api:${versions.jqwik}"
-    testRuntimeOnly    "net.jqwik:jqwik-engine:${versions.jqwik}"
-    testImplementation 
"org.junit.jupiter:junit-jupiter-params:${versions.junit5}"
-    testRuntimeOnly    
"org.junit.platform:junit-platform-runner:${versions.junit5Platform}"
+    testImplementation 
"org.junit.jupiter:junit-jupiter-params:${versions.junit6}"
 }
diff --git 
a/subprojects/groovy-test-junit6/src/main/groovy/groovy/junit6/plugin/GroovyJUnitRunnerHelper.groovy
 
b/subprojects/groovy-test-junit6/src/main/groovy/groovy/junit6/plugin/GroovyJUnitRunnerHelper.groovy
new file mode 100644
index 0000000000..06ef0a6288
--- /dev/null
+++ 
b/subprojects/groovy-test-junit6/src/main/groovy/groovy/junit6/plugin/GroovyJUnitRunnerHelper.groovy
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ */
+package groovy.junit6.plugin
+
+import org.junit.platform.launcher.Launcher
+import org.junit.platform.launcher.LauncherDiscoveryRequest
+import org.junit.platform.launcher.TestExecutionListener
+import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder
+import org.junit.platform.launcher.core.LauncherFactory
+import org.junit.platform.launcher.listeners.LoggingListener
+import org.junit.platform.launcher.listeners.SummaryGeneratingListener
+
+import static 
org.junit.platform.engine.discovery.DiscoverySelectors.selectClass
+
+class GroovyJUnitRunnerHelper {
+    @SuppressWarnings('Println')
+    static Throwable execute(Class testClass) {
+        LauncherDiscoveryRequest request = 
LauncherDiscoveryRequestBuilder.request()
+                .selectors(selectClass(testClass)).build()
+        Launcher launcher = LauncherFactory.create()
+
+        TestExecutionListener listener = new SummaryGeneratingListener()
+        launcher.registerTestExecutionListeners(listener)
+        
launcher.registerTestExecutionListeners(LoggingListener.forJavaUtilLogging())
+        launcher.execute(request)
+        println listener.summary.with{ "JUnit6 launcher: 
passed=$testsSucceededCount, aborted=$testsAbortedCount, 
failed=$testsFailedCount, skipped=$testsSkippedCount, 
time=${timeFinished-timeStarted}ms" }
+        if (listener.summary.failures) {
+            listener.summary.printFailuresTo(new PrintWriter(System.out, true))
+            return listener.summary.failures[0].exception
+        }
+        null
+    }
+}
diff --git 
a/subprojects/groovy-test-junit6/src/main/java/groovy/junit6/plugin/JUnit6Runner.java
 
b/subprojects/groovy-test-junit6/src/main/java/groovy/junit6/plugin/JUnit6Runner.java
new file mode 100644
index 0000000000..d372964b10
--- /dev/null
+++ 
b/subprojects/groovy-test-junit6/src/main/java/groovy/junit6/plugin/JUnit6Runner.java
@@ -0,0 +1,136 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ */
+package groovy.junit6.plugin;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyRuntimeException;
+import org.apache.groovy.plugin.GroovyRunner;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Integration code for running JUnit 6 (Jupiter) tests in Groovy.
+ */
+public class JUnit6Runner implements GroovyRunner {
+
+    private static final System.Logger LOGGER = 
System.getLogger(JUnit6Runner.class.getName());
+
+    /**
+     * Utility method to check via reflection if the parsed class appears to 
be a JUnit Jupiter
+     * test, i.e. checks whether it appears to be using the relevant 
annotations.
+     *
+     * @param scriptClass the class we want to check
+     * @param loader the GroovyClassLoader to use to find classes
+     * @return true if the class appears to be a compatible test
+     */
+    @Override
+    public boolean canRun(Class<?> scriptClass, GroovyClassLoader loader) {
+        if (!tryLoadClass("org.junit.jupiter.api.Test", loader)) {
+            return false;
+        }
+        if (isJupiterAnnotationPresent(scriptClass.getAnnotations(), loader)) {
+            return true;
+        }
+        Method[] methods = scriptClass.getMethods();
+        for (Method method : methods) {
+            if (isJupiterAnnotationPresent(method.getAnnotations(), loader)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isJupiterAnnotationPresent(Annotation[] annotations, 
GroovyClassLoader loader) {
+        for (Annotation annotation : annotations) {
+            Class<? extends Annotation> type = annotation.annotationType();
+            String name = type.getName();
+            if (name.startsWith("org.junit.jupiter.api.") && 
tryLoadClass(name, loader)) {
+                return true;
+            }
+            // it might directly annotate a class, e.g. Specification in Spock 
2
+            if ("org.junit.platform.commons.annotation.Testable".equals(name) 
&& tryLoadClass(name, loader)) {
+                return true;
+            }
+            if (isJupiterTestableMetaAnnotationPresent(type, new HashSet<>()) 
&& tryLoadClass(name, loader)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isJupiterTestableMetaAnnotationPresent(Class<? extends 
Annotation> type, Set<String> seen) {
+        for (Annotation annotation : type.getAnnotations()) {
+            // It might annotate an annotation, e.g. jqwik @Property
+            if 
("org.junit.platform.commons.annotation.Testable".equals(annotation.annotationType().getName()))
 {
+                return true;
+            }
+            // perhaps it's a meta-annotation, e.g. jqwik @Example is 
annotated with @Property
+            if (!seen.contains(annotation.annotationType().getName())) {
+                seen.add(annotation.annotationType().getName());
+                if 
(isJupiterTestableMetaAnnotationPresent(annotation.annotationType(), seen)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean tryLoadClass(String name, GroovyClassLoader loader) {
+        try {
+            loader.loadClass(name);
+            return true;
+        } catch (ClassNotFoundException ignore) {
+            // fall through
+        }
+        return false;
+    }
+
+    /**
+     * Utility method to run a JUnit 6 test.
+     *
+     * @param scriptClass the class we want to run as a test
+     * @param loader the class loader to use
+     * @return the result of running the test
+     */
+    @Override
+    public Object run(Class<?> scriptClass, GroovyClassLoader loader) {
+        try {
+            try {
+                
loader.loadClass("org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder");
+            } catch (ClassNotFoundException ignored) {
+                // subsequent steps will bomb out but try to give some more 
friendly information first
+                LOGGER.log(System.Logger.Level.WARNING,
+                        "Required dependency: 
org.junit.platform:junit-platform-launcher doesn't appear to be on the 
classpath");
+            }
+            Class<?> helper = 
loader.loadClass("groovy.junit6.plugin.GroovyJUnitRunnerHelper");
+            Throwable ifFailed = (Throwable) 
InvokerHelper.invokeStaticMethod(helper, "execute", new Object[]{scriptClass});
+            if (ifFailed != null) {
+                throw new GroovyRuntimeException(ifFailed);
+            }
+            return null;
+        } catch (ClassNotFoundException e) {
+            throw new GroovyRuntimeException("Error running JUnit 6 test.", e);
+        }
+    }
+
+}
diff --git 
a/subprojects/groovy-test-junit6/src/main/java/groovy/junit6/plugin/package-info.java
 
b/subprojects/groovy-test-junit6/src/main/java/groovy/junit6/plugin/package-info.java
new file mode 100644
index 0000000000..4f3f2441a8
--- /dev/null
+++ 
b/subprojects/groovy-test-junit6/src/main/java/groovy/junit6/plugin/package-info.java
@@ -0,0 +1,23 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.
+ */
+
+/**
+ * Classes to support running JUnit 6 (Jupiter) tests as scripts.
+ */
+package groovy.junit6.plugin;
diff --git 
a/subprojects/groovy-test-junit6/src/main/resources/META-INF/services/org.apache.groovy.plugin.GroovyRunner
 
b/subprojects/groovy-test-junit6/src/main/resources/META-INF/services/org.apache.groovy.plugin.GroovyRunner
new file mode 100644
index 0000000000..a1c1717030
--- /dev/null
+++ 
b/subprojects/groovy-test-junit6/src/main/resources/META-INF/services/org.apache.groovy.plugin.GroovyRunner
@@ -0,0 +1,20 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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.
+#
+
+groovy.junit6.plugin.JUnit6Runner
diff --git a/subprojects/groovy-test-junit6/src/test/groovy/JUnit6Test.groovy 
b/subprojects/groovy-test-junit6/src/test/groovy/JUnit6Test.groovy
new file mode 100644
index 0000000000..3afc58c81f
--- /dev/null
+++ b/subprojects/groovy-test-junit6/src/test/groovy/JUnit6Test.groovy
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.junit.jupiter.api.RepeatedTest
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.TestFactory
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+
+import java.util.stream.Stream
+
+import static org.junit.jupiter.api.Assertions.assertTrue
+import static org.junit.jupiter.api.DynamicTest.dynamicTest
+
+class JUnit6Test {
+
+    @Test
+    void streamSum() {
+        assertTrue(Stream.of(1, 2, 3)
+                .mapToInt(i -> i)
+                .sum() > 5, () -> "Sum should be greater than 5")
+    }
+
+    @RepeatedTest(value = 2, name = "{displayName} 
{currentRepetition}/{totalRepetitions}")
+    void streamSumRepeated() {
+        assert Stream.of(1, 2, 3).mapToInt(i -> i).sum() == 6
+    }
+
+    private boolean isPalindrome(s) { s == s.reverse() }
+
+    @ParameterizedTest
+    @ValueSource(strings = ["racecar", "radar", "able was I ere I saw elba"])
+    void palindromes(String candidate) {
+        assert isPalindrome(candidate)
+    }
+
+    @TestFactory
+    def dynamicTestCollection() {
+        [
+                dynamicTest("Add test") { -> assert 1 + 1 == 2 },
+                dynamicTest("Multiply Test", () -> { assert 2 * 3 == 6 })
+        ]
+    }
+}
diff --git a/versions.properties b/versions.properties
index 1ff26f3bee..33e43145b1 100644
--- a/versions.properties
+++ b/versions.properties
@@ -45,6 +45,7 @@ jruby=9.2.17.0
 junit=4.13.2
 junit5=5.14.3
 junit5Platform=1.14.3
+junit6=6.0.3
 log4j=1.2.17
 log4j2=2.25.3
 logback=1.5.32

Reply via email to