From 3afdf0a5f0263f6546a91e463427453b59ddaeaa Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Tue, 19 Aug 2025 13:42:09 +0200 Subject: [PATCH 01/25] Introduce experimental junit-onramp module --- build.gradle.kts | 2 +- junit-onramp/junit-onramp.gradle.kts | 39 ++++++++ junit-onramp/src/main/java/module-info.java | 41 ++++++++ .../onramp/ContainerFeedPrintingListener.java | 94 ++++++++++++++++++ .../src/main/java/org/junit/onramp/JUnit.java | 95 +++++++++++++++++++ .../java/org/junit/onramp/ModuleSupport.java | 55 +++++++++++ .../java/org/junit/onramp/package-info.java | 8 ++ .../platform/commons/util/ExceptionUtils.java | 5 + .../junit-onramp.expected.txt | 10 ++ .../platform/tooling/support/HelperTests.java | 1 + settings.gradle.kts | 1 + 11 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 junit-onramp/junit-onramp.gradle.kts create mode 100644 junit-onramp/src/main/java/module-info.java create mode 100644 junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java create mode 100644 junit-onramp/src/main/java/org/junit/onramp/JUnit.java create mode 100644 junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java create mode 100644 junit-onramp/src/main/java/org/junit/onramp/package-info.java create mode 100644 platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt diff --git a/build.gradle.kts b/build.gradle.kts index ac2b1c43ec54..f3faf92317ff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,7 +43,7 @@ val vintageProjects by extra(listOf( dependencyProject(projects.junitVintageEngine) )) -val mavenizedProjects by extra(platformProjects + jupiterProjects + vintageProjects) +val mavenizedProjects by extra(listOf(dependencyProject(projects.junitOnramp)) + platformProjects + jupiterProjects + vintageProjects) val modularProjects by extra(mavenizedProjects - setOf(dependencyProject(projects.junitPlatformConsoleStandalone))) dependencies { diff --git a/junit-onramp/junit-onramp.gradle.kts b/junit-onramp/junit-onramp.gradle.kts new file mode 100644 index 000000000000..5d2da92a8838 --- /dev/null +++ b/junit-onramp/junit-onramp.gradle.kts @@ -0,0 +1,39 @@ +import junitbuild.java.UpdateJarAction + +plugins { + id("junitbuild.java-library-conventions") + id("junitbuild.java-nullability-conventions") +} + +description = "JUnit On-Ramp Module" + +dependencies { + api(platform(projects.junitBom)) + api(projects.junitJupiter) + api(projects.junitPlatformLauncher) + + compileOnlyApi(libs.apiguardian) + compileOnlyApi(libs.jspecify) + compileOnlyApi(projects.junitJupiterEngine) + + implementation(projects.junitPlatformConsole) +} + +tasks { + jar { + manifest { + attributes("Main-Class" to "org.junit.onramp.JUnit") + } + doLast(objects.newInstance(UpdateJarAction::class).apply { + javaLauncher = project.javaToolchains.launcherFor(java.toolchain) + args.addAll( + "--file", archiveFile.get().asFile.absolutePath, + "--main-class", "org.junit.onramp.JUnit", + ) + }) + } +} + +japicmp { + enabled = false // no previous version, yet +} diff --git a/junit-onramp/src/main/java/module-info.java b/junit-onramp/src/main/java/module-info.java new file mode 100644 index 000000000000..97dbd8e79f8a --- /dev/null +++ b/junit-onramp/src/main/java/module-info.java @@ -0,0 +1,41 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +/** + * Defines the API of the JUnit On-Ramp module for writing and running tests. + *

+ * Usage example: + *

{@code
+ * import module org.junit.onramp;
+ *
+ *  void main() {
+ *    JUnit.run(this);
+ *  }
+ *
+ *  @Test
+ *  void addition() {
+ *    Assertions.assertEquals(2, 1 + 1, "Addition error detected!");
+ *  }
+ * }
+ */ +module org.junit.onramp { + + requires static transitive org.apiguardian.api; + requires static transitive org.jspecify; + + requires transitive org.junit.jupiter; + requires org.junit.platform.launcher; + requires org.junit.platform.console; + + exports org.junit.onramp; + + uses java.util.spi.ToolProvider; + +} diff --git a/junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java b/junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java new file mode 100644 index 000000000000..ccfd43a29422 --- /dev/null +++ b/junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.onramp; + +import java.util.Map; +import java.util.StringJoiner; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestIdentifier; +import org.junit.platform.launcher.TestPlan; + +class ContainerFeedPrintingListener implements TestExecutionListener { + + private record Summary(int size, int successful, int aborted, int failed, int skipped) { + Summary with(Summary other) { + return new Summary(size + other.size, successful + other.successful, aborted + other.aborted, + failed + other.failed, skipped + other.skipped); + } + } + + ContainerFeedPrintingListener() { + } + + private final Map summaries = new ConcurrentHashMap<>(); + + @Override + public void testPlanExecutionStarted(TestPlan testPlan) { + printCaptions(); // as header + summaries.put("/", new Summary(0, 0, 0, 0, 0)); + } + + @Override + public void testPlanExecutionFinished(TestPlan testPlan) { + if (summaries.size() > 42) { + printCaptions(); // as footer + } + } + + @Override + public void executionFinished(TestIdentifier identifier, TestExecutionResult result) { + if (identifier.isTest()) { + var status = result.getStatus(); + var summary = new Summary(1, status == TestExecutionResult.Status.SUCCESSFUL ? 1 : 0, + status == TestExecutionResult.Status.ABORTED ? 1 : 0, + status == TestExecutionResult.Status.FAILED ? 1 : 0, 0); + merge(identifier, summary); + } + if (identifier.isContainer()) { + var summary = summaries.get(identifier.getUniqueId()); + if (summary == null) + return; + var indent = " ".repeat(identifier.getUniqueIdObject().getSegments().size()); + var name = identifier.getDisplayName(); + print(summary, indent + name); + } + } + + @Override + public void executionSkipped(TestIdentifier identifier, String reason) { + if (identifier.isTest()) { + var summary = new Summary(1, 0, 0, 0, 1); + merge(identifier, summary); + } + } + + private void merge(TestIdentifier identifier, Summary summary) { + var joiner = new StringJoiner("/"); + var segments = identifier.getUniqueIdObject().getSegments(); + for (var segment : segments) { + joiner.add('[' + segment.getType() + ':' + segment.getValue() + ']'); + summaries.merge(joiner.toString(), summary, Summary::with); + } + } + + private void printCaptions() { + System.out.printf("%5s %5s %5s %5s %5s %s%n", // + "Found", "OK", "[A]", "[F]", "[S]", "Display Name"); + } + + private void print(Summary summary, String name) { + System.out.printf("%5s %5s %5s %5s %5s %s%n", // + summary.size, summary.successful, summary.aborted, summary.failed, summary.skipped, name); + } +} diff --git a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java new file mode 100644 index 000000000000..b4fc228b8307 --- /dev/null +++ b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java @@ -0,0 +1,95 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.onramp; + +import static org.apiguardian.api.API.Status.EXPERIMENTAL; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule; +import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request; + +import java.io.PrintWriter; +import java.nio.charset.Charset; +import java.util.function.UnaryOperator; +import java.util.spi.ToolProvider; + +import org.apiguardian.api.API; +import org.junit.platform.commons.JUnitException; +import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; +import org.junit.platform.launcher.core.LauncherFactory; +import org.junit.platform.launcher.listeners.SummaryGeneratingListener; + +@API(status = EXPERIMENTAL, since = "6.0") +public final class JUnit { + + private JUnit() { + } + + public static void run(Object instance) { + if (instance instanceof Class testClass) { + run(testClass); + return; + } + if (instance instanceof Module testModule) { + run(testModule); + return; + } + run(instance.getClass()); + } + + public static void run(Class testClass) { + run(discovery -> discovery.selectors(selectClass(testClass))); + } + + public enum ModuleSelectionMode { + PLATFORM_DEFAULT, AGGREGATOR_LOCAL + } + + public static void run(Module testModule) { + run(testModule, ModuleSelectionMode.PLATFORM_DEFAULT); + } + + public static void run(Module testModule, ModuleSelectionMode mode) { + switch (mode) { + case PLATFORM_DEFAULT -> run(discovery -> discovery.selectors(selectModule(testModule))); + case AGGREGATOR_LOCAL -> { + var selectors = ModuleSupport.listClassesInModule(testModule).stream() // + .map(DiscoverySelectors::selectClass).toList(); + run(discovery -> discovery.selectors(selectors)); + } + } + } + + // Don't transitively expose types from org.junit.platform.launcher module + private static void run(UnaryOperator discovery) { + var listener = new SummaryGeneratingListener(); + var request = discovery.apply(request()).forExecution() // + .listeners(listener, new ContainerFeedPrintingListener()) // + .build(); + var launcher = LauncherFactory.create(); + launcher.execute(request); + var summary = listener.getSummary(); + + if (summary.getTotalFailureCount() == 0) + return; + + summary.printFailuresTo(new PrintWriter(System.err, true, Charset.defaultCharset())); + throw new JUnitException("JUnit run finished with %d failure%s".formatted( // + summary.getTotalFailureCount(), // + summary.getTotalFailureCount() == 1 ? "" : "s")); + } + + public static void main(String[] args) { + var junit = ToolProvider.findFirst("junit").orElseThrow(); + var exitCode = junit.run(System.out, System.err, args); + System.exit(exitCode); + } +} diff --git a/junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java b/junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java new file mode 100644 index 000000000000..918241904125 --- /dev/null +++ b/junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java @@ -0,0 +1,55 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.onramp; + +import java.util.List; +import java.util.Optional; + +import org.junit.platform.commons.JUnitException; + +class ModuleSupport { + private ModuleSupport() { + } + + static final boolean SOURCE_MODE = System.getProperty("jdk.launcher.sourcefile") != null; + + static List> listClassesInModule(Module module) { + var resolved = module.getLayer().configuration().findModule(module.getName()).orElseThrow(); + try (var reader = resolved.reference().open()) { + return reader.list() // + .map(name -> loadClassByResourceName(module, name)) // + .flatMap(Optional::stream) // + .distinct() // + .toList(); + } + catch (Exception exception) { + throw new JUnitException("Listing classes in module %s failed".formatted(module), exception); + } + } + + static Optional> loadClassByResourceName(Module module, String name) { + var className = name; + if (SOURCE_MODE) { + if (name.endsWith(".java")) { + className = name.substring(0, name.length() - 5); + } + } + if (name.endsWith(".class")) { + className = name.substring(0, name.length() - 6); + } + try { + return Optional.of(Class.forName(module, className.replace('/', '.'))); + } + catch (Throwable ignored) { + return Optional.empty(); + } + } +} diff --git a/junit-onramp/src/main/java/org/junit/onramp/package-info.java b/junit-onramp/src/main/java/org/junit/onramp/package-info.java new file mode 100644 index 000000000000..421433db96a0 --- /dev/null +++ b/junit-onramp/src/main/java/org/junit/onramp/package-info.java @@ -0,0 +1,8 @@ +/** + * Contains JUnit On-Ramp API for writing and running tests. + */ + +@NullMarked +package org.junit.onramp; + +import org.jspecify.annotations.NullMarked; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java index 350f443b3ad1..8b03360f2ca5 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java @@ -41,6 +41,8 @@ @API(status = INTERNAL, since = "1.0") public final class ExceptionUtils { + private static final String JUNIT_ON_RAMP_PACKAGE_PREFIX = "org.junit.onramp."; + private static final String JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX = "org.junit.platform.launcher."; private static final Predicate STACK_TRACE_ELEMENT_FILTER = ClassNamePatternFilterUtils // @@ -139,6 +141,9 @@ public static void pruneStackTrace(Throwable throwable, List classNames) prunedStackTrace.addAll(stackTrace.subList(i, stackTrace.size())); break; } + else if (className.startsWith(JUNIT_ON_RAMP_PACKAGE_PREFIX)) { + prunedStackTrace.clear(); + } else if (className.startsWith(JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX)) { prunedStackTrace.clear(); } diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt new file mode 100644 index 000000000000..857bdac98779 --- /dev/null +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt @@ -0,0 +1,10 @@ +org.junit.onramp@${version} jar:file:.+/junit-onramp-\d.+\.jar..module-info\.class +exports org.junit.onramp +requires java.base mandated +requires org.apiguardian.api static transitive +requires org.jspecify static transitive +requires org.junit.jupiter transitive +requires org.junit.platform.console +requires org.junit.platform.launcher +uses java.util.spi.ToolProvider +main-class org.junit.onramp.JUnit diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java index d755f73cd5e8..998d3841cbcb 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java @@ -36,6 +36,7 @@ void loadModuleDirectoryNames() { "junit-jupiter-engine", // "junit-jupiter-migrationsupport", // "junit-jupiter-params", // + "junit-onramp", // "junit-platform-commons", // "junit-platform-console", // "junit-platform-engine", // diff --git a/settings.gradle.kts b/settings.gradle.kts index 53dc8d7c4f78..839b3bf5d5dd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -79,6 +79,7 @@ include("junit-jupiter-api") include("junit-jupiter-engine") include("junit-jupiter-migrationsupport") include("junit-jupiter-params") +include("junit-onramp") include("junit-platform-commons") include("junit-platform-console") include("junit-platform-console-standalone") From 2fdb1221c0047bd1aff25d36d6f5723172038100 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Thu, 9 Oct 2025 18:12:31 +0200 Subject: [PATCH 02/25] Reduce API surface --- junit-onramp/junit-onramp.gradle.kts | 18 +----- junit-onramp/src/main/java/module-info.java | 5 -- .../src/main/java/org/junit/onramp/JUnit.java | 55 ++++--------------- .../java/org/junit/onramp/ModuleSupport.java | 55 ------------------- .../junit-onramp.expected.txt | 3 - 5 files changed, 13 insertions(+), 123 deletions(-) delete mode 100644 junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java diff --git a/junit-onramp/junit-onramp.gradle.kts b/junit-onramp/junit-onramp.gradle.kts index 5d2da92a8838..5c01214162f7 100644 --- a/junit-onramp/junit-onramp.gradle.kts +++ b/junit-onramp/junit-onramp.gradle.kts @@ -10,28 +10,12 @@ description = "JUnit On-Ramp Module" dependencies { api(platform(projects.junitBom)) api(projects.junitJupiter) - api(projects.junitPlatformLauncher) compileOnlyApi(libs.apiguardian) compileOnlyApi(libs.jspecify) compileOnlyApi(projects.junitJupiterEngine) - implementation(projects.junitPlatformConsole) -} - -tasks { - jar { - manifest { - attributes("Main-Class" to "org.junit.onramp.JUnit") - } - doLast(objects.newInstance(UpdateJarAction::class).apply { - javaLauncher = project.javaToolchains.launcherFor(java.toolchain) - args.addAll( - "--file", archiveFile.get().asFile.absolutePath, - "--main-class", "org.junit.onramp.JUnit", - ) - }) - } + implementation(projects.junitPlatformLauncher) } japicmp { diff --git a/junit-onramp/src/main/java/module-info.java b/junit-onramp/src/main/java/module-info.java index 97dbd8e79f8a..d67aff4b99f0 100644 --- a/junit-onramp/src/main/java/module-info.java +++ b/junit-onramp/src/main/java/module-info.java @@ -26,16 +26,11 @@ * } */ module org.junit.onramp { - requires static transitive org.apiguardian.api; requires static transitive org.jspecify; requires transitive org.junit.jupiter; requires org.junit.platform.launcher; - requires org.junit.platform.console; exports org.junit.onramp; - - uses java.util.spi.ToolProvider; - } diff --git a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java index b4fc228b8307..251991b63396 100644 --- a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java +++ b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java @@ -17,13 +17,9 @@ import java.io.PrintWriter; import java.nio.charset.Charset; -import java.util.function.UnaryOperator; -import java.util.spi.ToolProvider; import org.apiguardian.api.API; import org.junit.platform.commons.JUnitException; -import org.junit.platform.engine.discovery.DiscoverySelectors; -import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.launcher.listeners.SummaryGeneratingListener; @@ -34,44 +30,23 @@ private JUnit() { } public static void run(Object instance) { + var listener = new SummaryGeneratingListener(); + var builder = request(); + if (instance instanceof Class testClass) { - run(testClass); - return; + // handling `JUnit.run(getClass())` + builder.selectors(selectClass(testClass)); } - if (instance instanceof Module testModule) { - run(testModule); - return; + else if (instance instanceof Module testModule) { + // handling `JUnit.run(getClass().getModule())` + builder.selectors(selectModule(testModule)); } - run(instance.getClass()); - } - - public static void run(Class testClass) { - run(discovery -> discovery.selectors(selectClass(testClass))); - } - - public enum ModuleSelectionMode { - PLATFORM_DEFAULT, AGGREGATOR_LOCAL - } - - public static void run(Module testModule) { - run(testModule, ModuleSelectionMode.PLATFORM_DEFAULT); - } - - public static void run(Module testModule, ModuleSelectionMode mode) { - switch (mode) { - case PLATFORM_DEFAULT -> run(discovery -> discovery.selectors(selectModule(testModule))); - case AGGREGATOR_LOCAL -> { - var selectors = ModuleSupport.listClassesInModule(testModule).stream() // - .map(DiscoverySelectors::selectClass).toList(); - run(discovery -> discovery.selectors(selectors)); - } + else { + // handling `JUnit.run(this)` + builder.selectors(selectClass(instance.getClass())); } - } - // Don't transitively expose types from org.junit.platform.launcher module - private static void run(UnaryOperator discovery) { - var listener = new SummaryGeneratingListener(); - var request = discovery.apply(request()).forExecution() // + var request = builder.forExecution() // .listeners(listener, new ContainerFeedPrintingListener()) // .build(); var launcher = LauncherFactory.create(); @@ -86,10 +61,4 @@ private static void run(UnaryOperator discovery summary.getTotalFailureCount(), // summary.getTotalFailureCount() == 1 ? "" : "s")); } - - public static void main(String[] args) { - var junit = ToolProvider.findFirst("junit").orElseThrow(); - var exitCode = junit.run(System.out, System.err, args); - System.exit(exitCode); - } } diff --git a/junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java b/junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java deleted file mode 100644 index 918241904125..000000000000 --- a/junit-onramp/src/main/java/org/junit/onramp/ModuleSupport.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015-2025 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.onramp; - -import java.util.List; -import java.util.Optional; - -import org.junit.platform.commons.JUnitException; - -class ModuleSupport { - private ModuleSupport() { - } - - static final boolean SOURCE_MODE = System.getProperty("jdk.launcher.sourcefile") != null; - - static List> listClassesInModule(Module module) { - var resolved = module.getLayer().configuration().findModule(module.getName()).orElseThrow(); - try (var reader = resolved.reference().open()) { - return reader.list() // - .map(name -> loadClassByResourceName(module, name)) // - .flatMap(Optional::stream) // - .distinct() // - .toList(); - } - catch (Exception exception) { - throw new JUnitException("Listing classes in module %s failed".formatted(module), exception); - } - } - - static Optional> loadClassByResourceName(Module module, String name) { - var className = name; - if (SOURCE_MODE) { - if (name.endsWith(".java")) { - className = name.substring(0, name.length() - 5); - } - } - if (name.endsWith(".class")) { - className = name.substring(0, name.length() - 6); - } - try { - return Optional.of(Class.forName(module, className.replace('/', '.'))); - } - catch (Throwable ignored) { - return Optional.empty(); - } - } -} diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt index 857bdac98779..abd0c182ecc7 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt @@ -4,7 +4,4 @@ requires java.base mandated requires org.apiguardian.api static transitive requires org.jspecify static transitive requires org.junit.jupiter transitive -requires org.junit.platform.console requires org.junit.platform.launcher -uses java.util.spi.ToolProvider -main-class org.junit.onramp.JUnit From e59b227bc8c8a70cb2fb9fabc90fe21af9602bf2 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 10 Oct 2025 14:59:57 +0200 Subject: [PATCH 03/25] Consider `*.java` source files as classes in source mode --- .../commons/util/ClasspathFilters.java | 16 ++++++++- .../commons/util/DefaultClasspathScanner.java | 36 +++++++++++-------- .../platform/commons/util/ModuleUtils.java | 16 +++------ 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java index 1887ed7a5211..4598cce8a483 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java @@ -19,11 +19,21 @@ class ClasspathFilters { static final String CLASS_FILE_SUFFIX = ".class"; + static final String SOURCE_FILE_SUFFIX = ".java"; + + // System property defined since Java 12: https://bugs.java/bugdatabase/JDK-8210877 + private static final boolean SOURCE_MODE = System.getProperty("jdk.launcher.sourcefile") != null; + private static final String PACKAGE_INFO_FILE_NAME = "package-info" + CLASS_FILE_SUFFIX; private static final String MODULE_INFO_FILE_NAME = "module-info" + CLASS_FILE_SUFFIX; + static boolean isClassOrSourceFileName(String name) { + return name.endsWith(CLASS_FILE_SUFFIX) || (SOURCE_MODE && name.endsWith(SOURCE_FILE_SUFFIX)); + } + static Predicate classFiles() { - return file -> isNotPackageInfo(file) && isNotModuleInfo(file) && isClassFile(file); + return file -> isNotPackageInfo(file) && isNotModuleInfo(file) + && (isClassFile(file) || (SOURCE_MODE && isSourceFile(file))); } static Predicate resourceFiles() { @@ -42,6 +52,10 @@ private static boolean isClassFile(Path file) { return file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX); } + private static boolean isSourceFile(Path file) { + return file.getFileName().toString().endsWith(SOURCE_FILE_SUFFIX); + } + private ClasspathFilters() { } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java index e7a66df2a44d..1720d895055f 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java @@ -11,7 +11,6 @@ package org.junit.platform.commons.util; import static java.util.stream.Collectors.joining; -import static org.junit.platform.commons.util.ClasspathFilters.CLASS_FILE_SUFFIX; import static org.junit.platform.commons.util.StringUtils.isNotBlank; import java.io.IOException; @@ -32,6 +31,7 @@ import java.util.function.Supplier; import java.util.stream.Stream; +import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.function.Try; import org.junit.platform.commons.io.Resource; @@ -182,10 +182,10 @@ private static void walkFilesForUri(URI baseUri, Predicate filter, BiConsu } } - private void processClassFileSafely(Path baseDir, String basePackageName, ClassFilter classFilter, Path classFile, + private void processClassFileSafely(Path baseDir, String basePackageName, ClassFilter classFilter, Path file, Consumer> classConsumer) { try { - String fullyQualifiedClassName = determineFullyQualifiedClassName(baseDir, basePackageName, classFile); + String fullyQualifiedClassName = determineFullyQualifiedClassName(baseDir, basePackageName, file); if (classFilter.match(fullyQualifiedClassName)) { try { // @formatter:off @@ -196,12 +196,12 @@ private void processClassFileSafely(Path baseDir, String basePackageName, ClassF // @formatter:on } catch (InternalError internalError) { - handleInternalError(classFile, fullyQualifiedClassName, internalError); + handleInternalError(file, fullyQualifiedClassName, internalError); } } } catch (Throwable throwable) { - handleThrowable(classFile, throwable); + handleThrowable(file, throwable); } } @@ -221,12 +221,12 @@ private void processResourceFileSafely(Path baseDir, String basePackageName, Res } } - private String determineFullyQualifiedClassName(Path baseDir, String basePackageName, Path classFile) { + private String determineFullyQualifiedClassName(Path baseDir, String basePackageName, Path file) { // @formatter:off return Stream.of( basePackageName, - determineSubpackageName(baseDir, classFile), - determineSimpleClassName(classFile) + determineSubpackageName(baseDir, file), + determineSimpleClassName(file) ) .filter(value -> !value.isEmpty()) // Handle default package appropriately. .collect(joining(PACKAGE_SEPARATOR_STRING)); @@ -253,21 +253,29 @@ private String determineFullyQualifiedResourceName(Path baseDir, String basePack // @formatter:on } - private String determineSimpleClassName(Path classFile) { - String fileName = classFile.getFileName().toString(); - return fileName.substring(0, fileName.length() - CLASS_FILE_SUFFIX.length()); + private String determineSimpleClassName(Path file) { + String fileName = file.getFileName().toString(); + return determineSimpleClassName(fileName); + } + + static String determineSimpleClassName(String fileName) { + int lastDot = fileName.lastIndexOf('.'); + if (lastDot < 0) { + throw new JUnitException("Expected file name with file extension, but got: " + fileName); + } + return fileName.substring(0, lastDot); } private String determineSimpleResourceName(Path resourceFile) { return resourceFile.getFileName().toString(); } - private String determineSubpackageName(Path baseDir, Path classFile) { - Path relativePath = baseDir.relativize(classFile.getParent()); + private String determineSubpackageName(Path baseDir, Path file) { + Path relativePath = baseDir.relativize(file.getParent()); String pathSeparator = baseDir.getFileSystem().getSeparator(); String subpackageName = relativePath.toString().replace(pathSeparator, PACKAGE_SEPARATOR_STRING); if (subpackageName.endsWith(pathSeparator)) { - // Workaround for JDK bug: https://bugs.openjdk.java.net/browse/JDK-8153248 + // TODO: Remove workaround for JDK bug: https://bugs.openjdk.org/browse/JDK-8153248 subpackageName = subpackageName.substring(0, subpackageName.length() - pathSeparator.length()); } return subpackageName; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java index 9c0ae00120c4..5f74cdf0fe81 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java @@ -253,9 +253,10 @@ List> scan(ModuleReference reference) { try (ModuleReader reader = reference.open()) { try (Stream names = reader.list()) { // @formatter:off - return names.filter(name -> name.endsWith(".class")) - .map(this::className) - .filter(name -> !"module-info".equals(name)) + return names.filter(ClasspathFilters::isClassOrSourceFileName) + .map(DefaultClasspathScanner::determineSimpleClassName) + .map(name -> name.replace('/', '.')) + .filter(name -> !"package-info".equals(name)) .filter(classFilter::match) .> map(this::loadClassUnchecked) .filter(classFilter::match) @@ -268,15 +269,6 @@ List> scan(ModuleReference reference) { } } - /** - * Convert resource name to binary class name. - */ - private String className(String resourceName) { - resourceName = resourceName.substring(0, resourceName.length() - 6); // 6 = ".class".length() - resourceName = resourceName.replace('/', '.'); - return resourceName; - } - /** * Load class by its binary name. * From 8cdf2f55abb1f9591a3da23650390fe6d0823971 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 10 Oct 2025 15:01:17 +0200 Subject: [PATCH 04/25] Overload `run()` API and reuse console printing listener --- junit-onramp/junit-onramp.gradle.kts | 1 + junit-onramp/src/main/java/module-info.java | 3 +- .../onramp/ContainerFeedPrintingListener.java | 94 ------------------- .../src/main/java/org/junit/onramp/JUnit.java | 52 ++++++---- .../junit-platform-console.gradle.kts | 1 + .../src/main/java/module-info.java | 2 + .../junit-onramp.expected.txt | 1 + .../junit-platform-console.expected.txt | 2 +- 8 files changed, 41 insertions(+), 115 deletions(-) delete mode 100644 junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java diff --git a/junit-onramp/junit-onramp.gradle.kts b/junit-onramp/junit-onramp.gradle.kts index 5c01214162f7..e7fc8961f53f 100644 --- a/junit-onramp/junit-onramp.gradle.kts +++ b/junit-onramp/junit-onramp.gradle.kts @@ -16,6 +16,7 @@ dependencies { compileOnlyApi(projects.junitJupiterEngine) implementation(projects.junitPlatformLauncher) + implementation(projects.junitPlatformConsole) } japicmp { diff --git a/junit-onramp/src/main/java/module-info.java b/junit-onramp/src/main/java/module-info.java index d67aff4b99f0..0cf941f368cb 100644 --- a/junit-onramp/src/main/java/module-info.java +++ b/junit-onramp/src/main/java/module-info.java @@ -16,7 +16,7 @@ * import module org.junit.onramp; * * void main() { - * JUnit.run(this); + * JUnit.run(); * } * * @Test @@ -31,6 +31,7 @@ requires transitive org.junit.jupiter; requires org.junit.platform.launcher; + requires org.junit.platform.console; exports org.junit.onramp; } diff --git a/junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java b/junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java deleted file mode 100644 index ccfd43a29422..000000000000 --- a/junit-onramp/src/main/java/org/junit/onramp/ContainerFeedPrintingListener.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2015-2025 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.onramp; - -import java.util.Map; -import java.util.StringJoiner; -import java.util.concurrent.ConcurrentHashMap; - -import org.junit.platform.engine.TestExecutionResult; -import org.junit.platform.launcher.TestExecutionListener; -import org.junit.platform.launcher.TestIdentifier; -import org.junit.platform.launcher.TestPlan; - -class ContainerFeedPrintingListener implements TestExecutionListener { - - private record Summary(int size, int successful, int aborted, int failed, int skipped) { - Summary with(Summary other) { - return new Summary(size + other.size, successful + other.successful, aborted + other.aborted, - failed + other.failed, skipped + other.skipped); - } - } - - ContainerFeedPrintingListener() { - } - - private final Map summaries = new ConcurrentHashMap<>(); - - @Override - public void testPlanExecutionStarted(TestPlan testPlan) { - printCaptions(); // as header - summaries.put("/", new Summary(0, 0, 0, 0, 0)); - } - - @Override - public void testPlanExecutionFinished(TestPlan testPlan) { - if (summaries.size() > 42) { - printCaptions(); // as footer - } - } - - @Override - public void executionFinished(TestIdentifier identifier, TestExecutionResult result) { - if (identifier.isTest()) { - var status = result.getStatus(); - var summary = new Summary(1, status == TestExecutionResult.Status.SUCCESSFUL ? 1 : 0, - status == TestExecutionResult.Status.ABORTED ? 1 : 0, - status == TestExecutionResult.Status.FAILED ? 1 : 0, 0); - merge(identifier, summary); - } - if (identifier.isContainer()) { - var summary = summaries.get(identifier.getUniqueId()); - if (summary == null) - return; - var indent = " ".repeat(identifier.getUniqueIdObject().getSegments().size()); - var name = identifier.getDisplayName(); - print(summary, indent + name); - } - } - - @Override - public void executionSkipped(TestIdentifier identifier, String reason) { - if (identifier.isTest()) { - var summary = new Summary(1, 0, 0, 0, 1); - merge(identifier, summary); - } - } - - private void merge(TestIdentifier identifier, Summary summary) { - var joiner = new StringJoiner("/"); - var segments = identifier.getUniqueIdObject().getSegments(); - for (var segment : segments) { - joiner.add('[' + segment.getType() + ':' + segment.getValue() + ']'); - summaries.merge(joiner.toString(), summary, Summary::with); - } - } - - private void printCaptions() { - System.out.printf("%5s %5s %5s %5s %5s %s%n", // - "Found", "OK", "[A]", "[F]", "[S]", "Display Name"); - } - - private void print(Summary summary, String name) { - System.out.printf("%5s %5s %5s %5s %5s %s%n", // - summary.size, summary.successful, summary.aborted, summary.failed, summary.skipped, name); - } -} diff --git a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java index 251991b63396..3adfa0408e5e 100644 --- a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java +++ b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java @@ -10,6 +10,7 @@ package org.junit.onramp; +import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE; import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule; @@ -20,34 +21,44 @@ import org.apiguardian.api.API; import org.junit.platform.commons.JUnitException; +import org.junit.platform.console.output.ColorPalette; +import org.junit.platform.console.output.TestFeedPrintingListener; +import org.junit.platform.engine.DiscoverySelector; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.launcher.listeners.SummaryGeneratingListener; @API(status = EXPERIMENTAL, since = "6.0") public final class JUnit { - - private JUnit() { + /** + * Run all tests defined in the caller class. + */ + public static void run() { + var walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE); + run(selectClass(walker.getCallerClass())); } - public static void run(Object instance) { - var listener = new SummaryGeneratingListener(); - var builder = request(); + /** + * Run all tests defined in the given test class. + * @param testClass the class to discover and execute tests in + */ + public static void run(Class testClass) { + run(selectClass(testClass)); + } - if (instance instanceof Class testClass) { - // handling `JUnit.run(getClass())` - builder.selectors(selectClass(testClass)); - } - else if (instance instanceof Module testModule) { - // handling `JUnit.run(getClass().getModule())` - builder.selectors(selectModule(testModule)); - } - else { - // handling `JUnit.run(this)` - builder.selectors(selectClass(instance.getClass())); - } + /** + * Run all tests defined in the given module. + * @param testModule the module to discover and execute tests in + */ + public static void run(Module testModule) { + run(selectModule(testModule)); + } - var request = builder.forExecution() // - .listeners(listener, new ContainerFeedPrintingListener()) // + private static void run(DiscoverySelector selector) { + var listener = new SummaryGeneratingListener(); + var writer = new PrintWriter(System.out, true, Charset.defaultCharset()); + var printer = new TestFeedPrintingListener(writer, ColorPalette.DEFAULT); + var request = request().selectors(selector).forExecution() // + .listeners(listener, printer) // .build(); var launcher = LauncherFactory.create(); launcher.execute(request); @@ -61,4 +72,7 @@ else if (instance instanceof Module testModule) { summary.getTotalFailureCount(), // summary.getTotalFailureCount() == 1 ? "" : "s")); } + + private JUnit() { + } } diff --git a/junit-platform-console/junit-platform-console.gradle.kts b/junit-platform-console/junit-platform-console.gradle.kts index d3d9b59fec4f..2a5b9973b9bc 100644 --- a/junit-platform-console/junit-platform-console.gradle.kts +++ b/junit-platform-console/junit-platform-console.gradle.kts @@ -28,6 +28,7 @@ dependencies { tasks { compileJava { options.compilerArgs.addAll(listOf( + "-Xlint:-module", // due to qualified exports "--add-modules", "info.picocli", "--add-reads", "${javaModuleName}=info.picocli" )) diff --git a/junit-platform-console/src/main/java/module-info.java b/junit-platform-console/src/main/java/module-info.java index 336c201f8252..455d5dda7739 100644 --- a/junit-platform-console/src/main/java/module-info.java +++ b/junit-platform-console/src/main/java/module-info.java @@ -24,5 +24,7 @@ requires org.junit.platform.launcher; requires org.junit.platform.reporting; + exports org.junit.platform.console.output to org.junit.onramp; + provides java.util.spi.ToolProvider with org.junit.platform.console.ConsoleLauncherToolProvider; } diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt index abd0c182ecc7..5f4d706a4b7e 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt @@ -4,4 +4,5 @@ requires java.base mandated requires org.apiguardian.api static transitive requires org.jspecify static transitive requires org.junit.jupiter transitive +requires org.junit.platform.console requires org.junit.platform.launcher diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt index 1309f73a2f35..d459578a92fa 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt @@ -7,9 +7,9 @@ requires org.junit.platform.engine requires org.junit.platform.launcher requires org.junit.platform.reporting provides java.util.spi.ToolProvider with org.junit.platform.console.ConsoleLauncherToolProvider +qualified exports org.junit.platform.console.output to org.junit.onramp contains org.junit.platform.console contains org.junit.platform.console.command contains org.junit.platform.console.options -contains org.junit.platform.console.output contains org.junit.platform.console.shadow.picocli main-class org.junit.platform.console.ConsoleLauncher From 4d1f062f199dae15f1efa364976aa119e3c6656f Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 10 Oct 2025 16:07:20 +0200 Subject: [PATCH 05/25] Use tree printer by default --- junit-onramp/src/main/java/org/junit/onramp/JUnit.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java index 3adfa0408e5e..37fa18dba816 100644 --- a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java +++ b/junit-onramp/src/main/java/org/junit/onramp/JUnit.java @@ -22,7 +22,8 @@ import org.apiguardian.api.API; import org.junit.platform.commons.JUnitException; import org.junit.platform.console.output.ColorPalette; -import org.junit.platform.console.output.TestFeedPrintingListener; +import org.junit.platform.console.output.Theme; +import org.junit.platform.console.output.TreePrintingListener; import org.junit.platform.engine.DiscoverySelector; import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.launcher.listeners.SummaryGeneratingListener; @@ -55,8 +56,9 @@ public static void run(Module testModule) { private static void run(DiscoverySelector selector) { var listener = new SummaryGeneratingListener(); - var writer = new PrintWriter(System.out, true, Charset.defaultCharset()); - var printer = new TestFeedPrintingListener(writer, ColorPalette.DEFAULT); + var charset = Charset.defaultCharset(); + var writer = new PrintWriter(System.out, true, charset); + var printer = new TreePrintingListener(writer, ColorPalette.DEFAULT, Theme.valueOf(charset)); var request = request().selectors(selector).forExecution() // .listeners(listener, printer) // .build(); @@ -67,7 +69,7 @@ private static void run(DiscoverySelector selector) { if (summary.getTotalFailureCount() == 0) return; - summary.printFailuresTo(new PrintWriter(System.err, true, Charset.defaultCharset())); + summary.printFailuresTo(new PrintWriter(System.err, true, charset)); throw new JUnitException("JUnit run finished with %d failure%s".formatted( // summary.getTotalFailureCount(), // summary.getTotalFailureCount() == 1 ? "" : "s")); From f4ba4fcac1e363ee980f8189de38a93c3a56febb Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 10 Oct 2025 16:07:49 +0200 Subject: [PATCH 06/25] Skip module declaration files --- .../main/java/org/junit/platform/commons/util/ModuleUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java index 5f74cdf0fe81..f4e5c9eaedbf 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java @@ -256,7 +256,8 @@ List> scan(ModuleReference reference) { return names.filter(ClasspathFilters::isClassOrSourceFileName) .map(DefaultClasspathScanner::determineSimpleClassName) .map(name -> name.replace('/', '.')) - .filter(name -> !"package-info".equals(name)) + .filter(name -> !"module-info".equals(name)) + .filter(name -> !name.endsWith("package-info")) .filter(classFilter::match) .> map(this::loadClassUnchecked) .filter(classFilter::match) From ea1e6862d405ced685d10542fe364367980d020c Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Thu, 16 Oct 2025 13:54:38 +0200 Subject: [PATCH 07/25] Add tests using the `org.junit.onramp` module --- .../platform-tooling-support-tests.gradle.kts | 1 + .../junit-onramp/compact/JUnitRun.java | 20 +++ .../junit-onramp/compact/JUnitRunClass.java | 20 +++ .../junit-onramp/modular/module-info.java | 3 + .../modular/p/JUnitRunModule.java | 19 +++ .../modular/p/MultiplicationTests.java | 21 ++++ .../support/tests/JUnitOnRampTests.java | 115 ++++++++++++++++++ .../tooling/support/tests/Projects.java | 1 + 8 files changed, 200 insertions(+) create mode 100644 platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java create mode 100644 platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java create mode 100644 platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java create mode 100644 platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java create mode 100644 platform-tooling-support-tests/projects/junit-onramp/modular/p/MultiplicationTests.java create mode 100644 platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java diff --git a/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts b/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts index 220c012e77a8..2ba957fadfea 100644 --- a/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts +++ b/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts @@ -21,6 +21,7 @@ javaLibrary { spotless { java { target(files(project.java.sourceSets.map { it.allJava }), "projects/**/*.java") + targetExclude("projects/junit-onramp/**/*.java") // due to compact source files and module imports } kotlin { target("projects/**/*.kt") diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java new file mode 100644 index 000000000000..545377186955 --- /dev/null +++ b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java @@ -0,0 +1,20 @@ + +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ +import module org.junit.onramp; + +void main() { + JUnit.run(); +} + +@Test +void addition() { + Assertions.assertEquals(2, 1 + 1, "Addition error detected!"); +} diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java new file mode 100644 index 000000000000..deabac51f707 --- /dev/null +++ b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java @@ -0,0 +1,20 @@ + +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ +import module org.junit.onramp; + +void main() { + JUnit.run(getClass()); +} + +@Test +void substraction() { + Assertions.assertEquals(2, 3 - 1, "Subtraction error detected!"); +} diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java b/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java new file mode 100644 index 000000000000..926da191ad02 --- /dev/null +++ b/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java @@ -0,0 +1,3 @@ +open module m { + requires org.junit.onramp; +} diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java b/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java new file mode 100644 index 000000000000..f922d492de93 --- /dev/null +++ b/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java @@ -0,0 +1,19 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package p; + +import module org.junit.onramp; + +class JUnitRunModule { + void main() { + JUnit.run(getClass().getModule()); + } +} diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/p/MultiplicationTests.java b/platform-tooling-support-tests/projects/junit-onramp/modular/p/MultiplicationTests.java new file mode 100644 index 000000000000..af41e24566b3 --- /dev/null +++ b/platform-tooling-support-tests/projects/junit-onramp/modular/p/MultiplicationTests.java @@ -0,0 +1,21 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package p; + +import module org.junit.jupiter.api; + +class MultiplicationTests { + + @Test + void multiplication() { + Assertions.assertEquals(4, 2 * 2, "Multiplication error detected!"); + } +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java new file mode 100644 index 000000000000..1eea6590179e --- /dev/null +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package platform.tooling.support.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static platform.tooling.support.tests.Projects.copyToWorkspace; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; +import org.junit.jupiter.api.io.TempDir; +import org.junit.platform.tests.process.OutputFiles; + +import platform.tooling.support.Helper; +import platform.tooling.support.MavenRepo; +import platform.tooling.support.ProcessStarters; +import platform.tooling.support.ThirdPartyJars; + +/** + * @since 6.1 + */ +class JUnitOnRampTests { + + @TempDir + static Path workspace; + + @BeforeAll + static void prepareLocalLibraryDirectoryWithJUnitModules() throws Exception { + copyToWorkspace(Projects.JUNIT_ONRAMP, workspace); + var lib = workspace.resolve("lib"); + try { + Files.createDirectories(lib); + try (var directoryStream = Files.newDirectoryStream(lib, "*.jar")) { + for (Path jarFile : directoryStream) { + Files.delete(jarFile); + } + } + for (var module : Helper.loadModuleDirectoryNames()) { + if (module.startsWith("junit-platform") || module.startsWith("junit-jupiter") + || module.equals("junit-onramp")) { + if (module.equals("junit-jupiter-migrationsupport")) + continue; + if (module.startsWith("junit-platform-suite")) + continue; + if (module.equals("junit-platform-testkit")) + continue; + var jar = MavenRepo.jar(module); + Files.copy(jar, lib.resolve(module + ".jar")); + } + } + ThirdPartyJars.copy(lib, "org.apiguardian", "apiguardian-api"); + ThirdPartyJars.copy(lib, "org.jspecify", "jspecify"); + ThirdPartyJars.copy(lib, "org.opentest4j", "opentest4j"); + ThirdPartyJars.copy(lib, "org.opentest4j.reporting", "open-test-reporting-tooling-spi"); + } + catch (Exception e) { + throw new AssertionError("Preparing local library folder failed", e); + } + } + + @Test + @EnabledOnJre(JRE.JAVA_25) + void junitRun(@FilePrefix("junit-run") OutputFiles outputFiles) throws Exception { + var result = ProcessStarters.java() // + .workingDir(workspace) // + .addArguments("--module-path", "lib") // relative to workspace + .addArguments("--add-modules", "org.junit.onramp") // configure root module + .addArguments("compact/JUnitRun.java") // leverage Java's source mode + .redirectOutput(outputFiles) // + .startAndWait(); + + assertEquals(0, result.exitCode()); + } + + @Test + @EnabledOnJre(JRE.JAVA_25) + void junitRunClass(@FilePrefix("junit-run-class") OutputFiles outputFiles) throws Exception { + var result = ProcessStarters.java() // + .workingDir(workspace) // + .addArguments("--module-path", "lib") // relative to workspace + .addArguments("--add-modules", "org.junit.onramp") // configure root module + .addArguments("compact/JUnitRunClass.java") // leverage Java's source mode + .redirectOutput(outputFiles) // + .startAndWait(); + + assertEquals(0, result.exitCode()); + } + + @Test + @EnabledOnJre(JRE.JAVA_25) + void junitRunModule(@FilePrefix("junit-run-Module") OutputFiles outputFiles) throws Exception { + var result = ProcessStarters.java() // + .workingDir(workspace) // + .putEnvironment("NO_COLOR", "1") // --disable-ansi-colors + .addArguments("--module-path", "lib") // relative to workspace + .addArguments("modular/p/JUnitRunModule.java") // leverage Java's source mode + .redirectOutput(outputFiles) // + .startAndWait(); + + assertEquals(0, result.exitCode()); + } + +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java index be645466ee34..fe3784fea63e 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java @@ -21,6 +21,7 @@ public class Projects { public static final String GRADLE_KOTLIN_EXTENSIONS = "gradle-kotlin-extensions"; public static final String GRADLE_MISSING_ENGINE = "gradle-missing-engine"; public static final String JAR_DESCRIBE_MODULE = "jar-describe-module"; + public static final String JUNIT_ONRAMP = "junit-onramp"; public static final String JUPITER_STARTER = "jupiter-starter"; public static final String KOTLIN_COROUTINES = "kotlin-coroutines"; public static final String MAVEN_SUREFIRE_COMPATIBILITY = "maven-surefire-compatibility"; From 0eb6fa0f0c59267b33b6ed8529e0872517e1b8c6 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Thu, 16 Oct 2025 14:08:47 +0200 Subject: [PATCH 08/25] Fix copyright headers --- .../projects/junit-onramp/compact/JUnitRun.java | 2 +- .../projects/junit-onramp/compact/JUnitRunClass.java | 2 +- .../projects/junit-onramp/modular/module-info.java | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java index 545377186955..0f8e566d20da 100644 --- a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java +++ b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java @@ -1,4 +1,3 @@ - /* * Copyright 2015-2025 the original author or authors. * @@ -8,6 +7,7 @@ * * https://www.eclipse.org/legal/epl-v20.html */ + import module org.junit.onramp; void main() { diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java index deabac51f707..7c45eefbcaf8 100644 --- a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java +++ b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java @@ -1,4 +1,3 @@ - /* * Copyright 2015-2025 the original author or authors. * @@ -8,6 +7,7 @@ * * https://www.eclipse.org/legal/epl-v20.html */ + import module org.junit.onramp; void main() { diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java b/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java index 926da191ad02..e92522a87ff1 100644 --- a/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java +++ b/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java @@ -1,3 +1,13 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + open module m { requires org.junit.onramp; } From a3c4b611004a743a3a835e3d3d09c350c96673f7 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Thu, 16 Oct 2025 15:40:22 +0200 Subject: [PATCH 09/25] Minor cleanup --- junit-onramp/junit-onramp.gradle.kts | 3 --- 1 file changed, 3 deletions(-) diff --git a/junit-onramp/junit-onramp.gradle.kts b/junit-onramp/junit-onramp.gradle.kts index e7fc8961f53f..963327ca7c60 100644 --- a/junit-onramp/junit-onramp.gradle.kts +++ b/junit-onramp/junit-onramp.gradle.kts @@ -1,8 +1,5 @@ -import junitbuild.java.UpdateJarAction - plugins { id("junitbuild.java-library-conventions") - id("junitbuild.java-nullability-conventions") } description = "JUnit On-Ramp Module" From f54261613f5ca5af9baa7aa6d25ae36eac128e1b Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Thu, 16 Oct 2025 23:20:00 +0200 Subject: [PATCH 10/25] Fix configuration --- junit-onramp/junit-onramp.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/junit-onramp/junit-onramp.gradle.kts b/junit-onramp/junit-onramp.gradle.kts index 963327ca7c60..819923f69d97 100644 --- a/junit-onramp/junit-onramp.gradle.kts +++ b/junit-onramp/junit-onramp.gradle.kts @@ -16,6 +16,6 @@ dependencies { implementation(projects.junitPlatformConsole) } -japicmp { - enabled = false // no previous version, yet +backwardCompatibilityChecks { + enabled = false // already checked by individual projects } From 2308620ecb8e29dc6c5780d98c36933f1e691148 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 17 Oct 2025 18:19:06 +0200 Subject: [PATCH 11/25] Rename to `org.junit.start` --- build.gradle.kts | 2 +- .../src/main/java/org/junit/onramp/package-info.java | 8 -------- .../junit/platform/commons/util/ExceptionUtils.java | 4 ++-- junit-platform-console/src/main/java/module-info.java | 2 +- .../junit-start.gradle.kts | 2 +- .../src/main/java/module-info.java | 8 ++++---- .../src/main/java/org/junit/start}/JUnit.java | 2 +- .../src/main/java/org/junit/start/package-info.java | 8 ++++++++ .../platform-tooling-support-tests.gradle.kts | 2 +- .../jar-describe-module/junit-onramp.expected.txt | 4 ++-- .../junit-platform-console.expected.txt | 2 +- .../projects/junit-onramp/compact/JUnitRun.java | 2 +- .../projects/junit-onramp/compact/JUnitRunClass.java | 2 +- .../projects/junit-onramp/modular/module-info.java | 2 +- .../junit-onramp/modular/p/JUnitRunModule.java | 2 +- .../java/platform/tooling/support/HelperTests.java | 2 +- .../tooling/support/tests/JUnitOnRampTests.java | 10 +++++----- .../java/platform/tooling/support/tests/Projects.java | 2 +- settings.gradle.kts | 2 +- 19 files changed, 34 insertions(+), 34 deletions(-) delete mode 100644 junit-onramp/src/main/java/org/junit/onramp/package-info.java rename junit-onramp/junit-onramp.gradle.kts => junit-start/junit-start.gradle.kts (92%) rename {junit-onramp => junit-start}/src/main/java/module-info.java (81%) rename {junit-onramp/src/main/java/org/junit/onramp => junit-start/src/main/java/org/junit/start}/JUnit.java (99%) create mode 100644 junit-start/src/main/java/org/junit/start/package-info.java diff --git a/build.gradle.kts b/build.gradle.kts index f3faf92317ff..917c24b1805a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,7 +43,7 @@ val vintageProjects by extra(listOf( dependencyProject(projects.junitVintageEngine) )) -val mavenizedProjects by extra(listOf(dependencyProject(projects.junitOnramp)) + platformProjects + jupiterProjects + vintageProjects) +val mavenizedProjects by extra(listOf(dependencyProject(projects.junitStart)) + platformProjects + jupiterProjects + vintageProjects) val modularProjects by extra(mavenizedProjects - setOf(dependencyProject(projects.junitPlatformConsoleStandalone))) dependencies { diff --git a/junit-onramp/src/main/java/org/junit/onramp/package-info.java b/junit-onramp/src/main/java/org/junit/onramp/package-info.java deleted file mode 100644 index 421433db96a0..000000000000 --- a/junit-onramp/src/main/java/org/junit/onramp/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Contains JUnit On-Ramp API for writing and running tests. - */ - -@NullMarked -package org.junit.onramp; - -import org.jspecify.annotations.NullMarked; diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java index 8b03360f2ca5..a236b89fc90e 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java @@ -41,7 +41,7 @@ @API(status = INTERNAL, since = "1.0") public final class ExceptionUtils { - private static final String JUNIT_ON_RAMP_PACKAGE_PREFIX = "org.junit.onramp."; + private static final String JUNIT_START_PACKAGE_PREFIX = "org.junit.start."; private static final String JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX = "org.junit.platform.launcher."; @@ -141,7 +141,7 @@ public static void pruneStackTrace(Throwable throwable, List classNames) prunedStackTrace.addAll(stackTrace.subList(i, stackTrace.size())); break; } - else if (className.startsWith(JUNIT_ON_RAMP_PACKAGE_PREFIX)) { + else if (className.startsWith(JUNIT_START_PACKAGE_PREFIX)) { prunedStackTrace.clear(); } else if (className.startsWith(JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX)) { diff --git a/junit-platform-console/src/main/java/module-info.java b/junit-platform-console/src/main/java/module-info.java index 455d5dda7739..7ca71d3518dd 100644 --- a/junit-platform-console/src/main/java/module-info.java +++ b/junit-platform-console/src/main/java/module-info.java @@ -24,7 +24,7 @@ requires org.junit.platform.launcher; requires org.junit.platform.reporting; - exports org.junit.platform.console.output to org.junit.onramp; + exports org.junit.platform.console.output to org.junit.start; provides java.util.spi.ToolProvider with org.junit.platform.console.ConsoleLauncherToolProvider; } diff --git a/junit-onramp/junit-onramp.gradle.kts b/junit-start/junit-start.gradle.kts similarity index 92% rename from junit-onramp/junit-onramp.gradle.kts rename to junit-start/junit-start.gradle.kts index 819923f69d97..a86df06f0f85 100644 --- a/junit-onramp/junit-onramp.gradle.kts +++ b/junit-start/junit-start.gradle.kts @@ -2,7 +2,7 @@ plugins { id("junitbuild.java-library-conventions") } -description = "JUnit On-Ramp Module" +description = "JUnit Start Module" dependencies { api(platform(projects.junitBom)) diff --git a/junit-onramp/src/main/java/module-info.java b/junit-start/src/main/java/module-info.java similarity index 81% rename from junit-onramp/src/main/java/module-info.java rename to junit-start/src/main/java/module-info.java index 0cf941f368cb..9917e8569067 100644 --- a/junit-onramp/src/main/java/module-info.java +++ b/junit-start/src/main/java/module-info.java @@ -9,11 +9,11 @@ */ /** - * Defines the API of the JUnit On-Ramp module for writing and running tests. + * Defines the API of the JUnit Start module for writing and running tests. *

* Usage example: *

{@code
- * import module org.junit.onramp;
+ * import module org.junit.start;
  *
  *  void main() {
  *    JUnit.run();
@@ -25,7 +25,7 @@
  *  }
  * }
*/ -module org.junit.onramp { +module org.junit.start { requires static transitive org.apiguardian.api; requires static transitive org.jspecify; @@ -33,5 +33,5 @@ requires org.junit.platform.launcher; requires org.junit.platform.console; - exports org.junit.onramp; + exports org.junit.start; } diff --git a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java b/junit-start/src/main/java/org/junit/start/JUnit.java similarity index 99% rename from junit-onramp/src/main/java/org/junit/onramp/JUnit.java rename to junit-start/src/main/java/org/junit/start/JUnit.java index 37fa18dba816..c01ba51ff860 100644 --- a/junit-onramp/src/main/java/org/junit/onramp/JUnit.java +++ b/junit-start/src/main/java/org/junit/start/JUnit.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -package org.junit.onramp; +package org.junit.start; import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE; import static org.apiguardian.api.API.Status.EXPERIMENTAL; diff --git a/junit-start/src/main/java/org/junit/start/package-info.java b/junit-start/src/main/java/org/junit/start/package-info.java new file mode 100644 index 000000000000..f6b2a0ce7dda --- /dev/null +++ b/junit-start/src/main/java/org/junit/start/package-info.java @@ -0,0 +1,8 @@ +/** + * Contains JUnit Start API for writing and running tests. + */ + +@NullMarked +package org.junit.start; + +import org.jspecify.annotations.NullMarked; diff --git a/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts b/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts index 88fea0e545b4..46681ff3d45c 100644 --- a/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts +++ b/platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts @@ -20,7 +20,7 @@ javaLibrary { spotless { java { target(files(project.java.sourceSets.map { it.allJava }), "projects/**/*.java") - targetExclude("projects/junit-onramp/**/*.java") // due to compact source files and module imports + targetExclude("projects/junit-start/**/*.java") // due to compact source files and module imports } kotlin { target("projects/**/*.kt") diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt index 5f4d706a4b7e..0c4ee8aa39b8 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt @@ -1,5 +1,5 @@ -org.junit.onramp@${version} jar:file:.+/junit-onramp-\d.+\.jar..module-info\.class -exports org.junit.onramp +org.junit.start@${version} jar:file:.+/junit-start-\d.+\.jar..module-info\.class +exports org.junit.start requires java.base mandated requires org.apiguardian.api static transitive requires org.jspecify static transitive diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt index d459578a92fa..870d9bd85bd8 100644 --- a/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt +++ b/platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt @@ -7,7 +7,7 @@ requires org.junit.platform.engine requires org.junit.platform.launcher requires org.junit.platform.reporting provides java.util.spi.ToolProvider with org.junit.platform.console.ConsoleLauncherToolProvider -qualified exports org.junit.platform.console.output to org.junit.onramp +qualified exports org.junit.platform.console.output to org.junit.start contains org.junit.platform.console contains org.junit.platform.console.command contains org.junit.platform.console.options diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java index 0f8e566d20da..8d38641fb08f 100644 --- a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java +++ b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -import module org.junit.onramp; +import module org.junit.start; void main() { JUnit.run(); diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java index 7c45eefbcaf8..86e76c0d6523 100644 --- a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java +++ b/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java @@ -8,7 +8,7 @@ * https://www.eclipse.org/legal/epl-v20.html */ -import module org.junit.onramp; +import module org.junit.start; void main() { JUnit.run(getClass()); diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java b/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java index e92522a87ff1..9f29b2f31b7f 100644 --- a/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java +++ b/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java @@ -9,5 +9,5 @@ */ open module m { - requires org.junit.onramp; + requires org.junit.start; } diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java b/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java index f922d492de93..bde03f682da9 100644 --- a/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java +++ b/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java @@ -10,7 +10,7 @@ package p; -import module org.junit.onramp; +import module org.junit.start; class JUnitRunModule { void main() { diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java index 998d3841cbcb..1d0dc9dea053 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java @@ -36,7 +36,7 @@ void loadModuleDirectoryNames() { "junit-jupiter-engine", // "junit-jupiter-migrationsupport", // "junit-jupiter-params", // - "junit-onramp", // + "junit-start", // "junit-platform-commons", // "junit-platform-console", // "junit-platform-engine", // diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java index 1eea6590179e..d3f1ecbdc94e 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java @@ -31,14 +31,14 @@ /** * @since 6.1 */ -class JUnitOnRampTests { +class JUnitstartTests { @TempDir static Path workspace; @BeforeAll static void prepareLocalLibraryDirectoryWithJUnitModules() throws Exception { - copyToWorkspace(Projects.JUNIT_ONRAMP, workspace); + copyToWorkspace(Projects.JUNIT_start, workspace); var lib = workspace.resolve("lib"); try { Files.createDirectories(lib); @@ -49,7 +49,7 @@ static void prepareLocalLibraryDirectoryWithJUnitModules() throws Exception { } for (var module : Helper.loadModuleDirectoryNames()) { if (module.startsWith("junit-platform") || module.startsWith("junit-jupiter") - || module.equals("junit-onramp")) { + || module.equals("junit-start")) { if (module.equals("junit-jupiter-migrationsupport")) continue; if (module.startsWith("junit-platform-suite")) @@ -76,7 +76,7 @@ void junitRun(@FilePrefix("junit-run") OutputFiles outputFiles) throws Exception var result = ProcessStarters.java() // .workingDir(workspace) // .addArguments("--module-path", "lib") // relative to workspace - .addArguments("--add-modules", "org.junit.onramp") // configure root module + .addArguments("--add-modules", "org.junit.start") // configure root module .addArguments("compact/JUnitRun.java") // leverage Java's source mode .redirectOutput(outputFiles) // .startAndWait(); @@ -90,7 +90,7 @@ void junitRunClass(@FilePrefix("junit-run-class") OutputFiles outputFiles) throw var result = ProcessStarters.java() // .workingDir(workspace) // .addArguments("--module-path", "lib") // relative to workspace - .addArguments("--add-modules", "org.junit.onramp") // configure root module + .addArguments("--add-modules", "org.junit.start") // configure root module .addArguments("compact/JUnitRunClass.java") // leverage Java's source mode .redirectOutput(outputFiles) // .startAndWait(); diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java index fe3784fea63e..b62b9ca99cf4 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java @@ -21,7 +21,7 @@ public class Projects { public static final String GRADLE_KOTLIN_EXTENSIONS = "gradle-kotlin-extensions"; public static final String GRADLE_MISSING_ENGINE = "gradle-missing-engine"; public static final String JAR_DESCRIBE_MODULE = "jar-describe-module"; - public static final String JUNIT_ONRAMP = "junit-onramp"; + public static final String JUNIT_start = "junit-start"; public static final String JUPITER_STARTER = "jupiter-starter"; public static final String KOTLIN_COROUTINES = "kotlin-coroutines"; public static final String MAVEN_SUREFIRE_COMPATIBILITY = "maven-surefire-compatibility"; diff --git a/settings.gradle.kts b/settings.gradle.kts index 839b3bf5d5dd..22cff4977cde 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -79,7 +79,7 @@ include("junit-jupiter-api") include("junit-jupiter-engine") include("junit-jupiter-migrationsupport") include("junit-jupiter-params") -include("junit-onramp") +include("junit-start") include("junit-platform-commons") include("junit-platform-console") include("junit-platform-console-standalone") From 4dd14d66702d1e4f71f87adda232967aff7f14e4 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 17 Oct 2025 19:06:46 +0200 Subject: [PATCH 12/25] More renaming --- .../{junit-onramp.expected.txt => junit-start.expected.txt} | 0 .../{junit-onramp => junit-start}/compact/JUnitRun.java | 0 .../{junit-onramp => junit-start}/compact/JUnitRunClass.java | 0 .../{junit-onramp => junit-start}/modular/module-info.java | 0 .../{junit-onramp => junit-start}/modular/p/JUnitRunModule.java | 0 .../modular/p/MultiplicationTests.java | 0 .../tests/{JUnitOnRampTests.java => JUnitStartTests.java} | 2 +- 7 files changed, 1 insertion(+), 1 deletion(-) rename platform-tooling-support-tests/projects/jar-describe-module/{junit-onramp.expected.txt => junit-start.expected.txt} (100%) rename platform-tooling-support-tests/projects/{junit-onramp => junit-start}/compact/JUnitRun.java (100%) rename platform-tooling-support-tests/projects/{junit-onramp => junit-start}/compact/JUnitRunClass.java (100%) rename platform-tooling-support-tests/projects/{junit-onramp => junit-start}/modular/module-info.java (100%) rename platform-tooling-support-tests/projects/{junit-onramp => junit-start}/modular/p/JUnitRunModule.java (100%) rename platform-tooling-support-tests/projects/{junit-onramp => junit-start}/modular/p/MultiplicationTests.java (100%) rename platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/{JUnitOnRampTests.java => JUnitStartTests.java} (99%) diff --git a/platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt b/platform-tooling-support-tests/projects/jar-describe-module/junit-start.expected.txt similarity index 100% rename from platform-tooling-support-tests/projects/jar-describe-module/junit-onramp.expected.txt rename to platform-tooling-support-tests/projects/jar-describe-module/junit-start.expected.txt diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java b/platform-tooling-support-tests/projects/junit-start/compact/JUnitRun.java similarity index 100% rename from platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRun.java rename to platform-tooling-support-tests/projects/junit-start/compact/JUnitRun.java diff --git a/platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java b/platform-tooling-support-tests/projects/junit-start/compact/JUnitRunClass.java similarity index 100% rename from platform-tooling-support-tests/projects/junit-onramp/compact/JUnitRunClass.java rename to platform-tooling-support-tests/projects/junit-start/compact/JUnitRunClass.java diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java b/platform-tooling-support-tests/projects/junit-start/modular/module-info.java similarity index 100% rename from platform-tooling-support-tests/projects/junit-onramp/modular/module-info.java rename to platform-tooling-support-tests/projects/junit-start/modular/module-info.java diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java b/platform-tooling-support-tests/projects/junit-start/modular/p/JUnitRunModule.java similarity index 100% rename from platform-tooling-support-tests/projects/junit-onramp/modular/p/JUnitRunModule.java rename to platform-tooling-support-tests/projects/junit-start/modular/p/JUnitRunModule.java diff --git a/platform-tooling-support-tests/projects/junit-onramp/modular/p/MultiplicationTests.java b/platform-tooling-support-tests/projects/junit-start/modular/p/MultiplicationTests.java similarity index 100% rename from platform-tooling-support-tests/projects/junit-onramp/modular/p/MultiplicationTests.java rename to platform-tooling-support-tests/projects/junit-start/modular/p/MultiplicationTests.java diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java similarity index 99% rename from platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java rename to platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java index d3f1ecbdc94e..41204a63b574 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitOnRampTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java @@ -31,7 +31,7 @@ /** * @since 6.1 */ -class JUnitstartTests { +class JUnitStartTests { @TempDir static Path workspace; From 61c678dc313bbfbea2df367fe4c07eafe4ebde13 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 17 Oct 2025 19:21:31 +0200 Subject: [PATCH 13/25] Assert test method names are printed --- .../java/platform/tooling/support/tests/JUnitStartTests.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java index 41204a63b574..42a58f21776a 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java @@ -11,6 +11,7 @@ package platform.tooling.support.tests; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static platform.tooling.support.tests.Projects.copyToWorkspace; import java.nio.file.Files; @@ -82,6 +83,7 @@ void junitRun(@FilePrefix("junit-run") OutputFiles outputFiles) throws Exception .startAndWait(); assertEquals(0, result.exitCode()); + assertTrue(result.stdOut().contains("addition()"), result.stdOut()); } @Test @@ -96,6 +98,7 @@ void junitRunClass(@FilePrefix("junit-run-class") OutputFiles outputFiles) throw .startAndWait(); assertEquals(0, result.exitCode()); + assertTrue(result.stdOut().contains("substraction()"), result.stdOut()); } @Test @@ -110,6 +113,7 @@ void junitRunModule(@FilePrefix("junit-run-Module") OutputFiles outputFiles) thr .startAndWait(); assertEquals(0, result.exitCode()); + assertTrue(result.stdOut().contains("multiplication()"), result.stdOut()); } } From 1e060d06e0fade8dc5ea4029f5a4e1d95e614e9d Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 29 Oct 2025 08:52:46 +0100 Subject: [PATCH 14/25] Check for `NO_COLOR` environment variable --- junit-start/src/main/java/org/junit/start/JUnit.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/junit-start/src/main/java/org/junit/start/JUnit.java b/junit-start/src/main/java/org/junit/start/JUnit.java index c01ba51ff860..80b9e5707a3b 100644 --- a/junit-start/src/main/java/org/junit/start/JUnit.java +++ b/junit-start/src/main/java/org/junit/start/JUnit.java @@ -58,7 +58,9 @@ private static void run(DiscoverySelector selector) { var listener = new SummaryGeneratingListener(); var charset = Charset.defaultCharset(); var writer = new PrintWriter(System.out, true, charset); - var printer = new TreePrintingListener(writer, ColorPalette.DEFAULT, Theme.valueOf(charset)); + var palette = System.getenv("NO_COLOR") != null ? ColorPalette.NONE : ColorPalette.DEFAULT; + var theme = Theme.valueOf(charset); + var printer = new TreePrintingListener(writer, palette, theme); var request = request().selectors(selector).forExecution() // .listeners(listener, printer) // .build(); From efb6cd862f8958d808571abdd0b71e6c6566c545 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Thu, 30 Oct 2025 12:36:05 +0100 Subject: [PATCH 15/25] Move more common logic into `SearchPathUtils` --- .../commons/util/ClasspathFilters.java | 62 ------------- .../commons/util/DefaultClasspathScanner.java | 23 ++--- .../platform/commons/util/ModuleUtils.java | 10 +-- .../commons/util/SearchPathUtils.java | 87 +++++++++++++++++++ .../support/tests/JUnitStartTests.java | 2 +- 5 files changed, 98 insertions(+), 86 deletions(-) delete mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java create mode 100644 junit-platform-commons/src/main/java/org/junit/platform/commons/util/SearchPathUtils.java diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java deleted file mode 100644 index 4598cce8a483..000000000000 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFilters.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2015-2025 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ - -package org.junit.platform.commons.util; - -import java.nio.file.Path; -import java.util.function.Predicate; - -/** - * @since 1.11 - */ -class ClasspathFilters { - - static final String CLASS_FILE_SUFFIX = ".class"; - static final String SOURCE_FILE_SUFFIX = ".java"; - - // System property defined since Java 12: https://bugs.java/bugdatabase/JDK-8210877 - private static final boolean SOURCE_MODE = System.getProperty("jdk.launcher.sourcefile") != null; - - private static final String PACKAGE_INFO_FILE_NAME = "package-info" + CLASS_FILE_SUFFIX; - private static final String MODULE_INFO_FILE_NAME = "module-info" + CLASS_FILE_SUFFIX; - - static boolean isClassOrSourceFileName(String name) { - return name.endsWith(CLASS_FILE_SUFFIX) || (SOURCE_MODE && name.endsWith(SOURCE_FILE_SUFFIX)); - } - - static Predicate classFiles() { - return file -> isNotPackageInfo(file) && isNotModuleInfo(file) - && (isClassFile(file) || (SOURCE_MODE && isSourceFile(file))); - } - - static Predicate resourceFiles() { - return file -> !isClassFile(file); - } - - private static boolean isNotPackageInfo(Path path) { - return !path.endsWith(PACKAGE_INFO_FILE_NAME); - } - - private static boolean isNotModuleInfo(Path path) { - return !path.endsWith(MODULE_INFO_FILE_NAME); - } - - private static boolean isClassFile(Path file) { - return file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX); - } - - private static boolean isSourceFile(Path file) { - return file.getFileName().toString().endsWith(SOURCE_FILE_SUFFIX); - } - - private ClasspathFilters() { - } - -} diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java index 1720d895055f..e19efc373bdf 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java @@ -11,6 +11,9 @@ package org.junit.platform.commons.util; import static java.util.stream.Collectors.joining; +import static org.junit.platform.commons.util.SearchPathUtils.PACKAGE_SEPARATOR_CHAR; +import static org.junit.platform.commons.util.SearchPathUtils.PACKAGE_SEPARATOR_STRING; +import static org.junit.platform.commons.util.SearchPathUtils.determineSimpleClassName; import static org.junit.platform.commons.util.StringUtils.isNotBlank; import java.io.IOException; @@ -31,7 +34,6 @@ import java.util.function.Supplier; import java.util.stream.Stream; -import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.PreconditionViolationException; import org.junit.platform.commons.function.Try; import org.junit.platform.commons.io.Resource; @@ -57,8 +59,6 @@ class DefaultClasspathScanner implements ClasspathScanner { private static final char CLASSPATH_RESOURCE_PATH_SEPARATOR = '/'; private static final String CLASSPATH_RESOURCE_PATH_SEPARATOR_STRING = String.valueOf( CLASSPATH_RESOURCE_PATH_SEPARATOR); - private static final char PACKAGE_SEPARATOR_CHAR = '.'; - private static final String PACKAGE_SEPARATOR_STRING = String.valueOf(PACKAGE_SEPARATOR_CHAR); /** * Malformed class name InternalError like reported in #401. @@ -132,7 +132,7 @@ private List> findClassesForUris(List baseUris, String basePackage private List> findClassesForUri(URI baseUri, String basePackageName, ClassFilter classFilter) { List> classes = new ArrayList<>(); // @formatter:off - walkFilesForUri(baseUri, ClasspathFilters.classFiles(), + walkFilesForUri(baseUri, SearchPathUtils::isClassOrSourceFile, (baseDir, file) -> processClassFileSafely(baseDir, basePackageName, classFilter, file, classes::add)); // @formatter:on @@ -156,7 +156,7 @@ private List findResourcesForUris(List baseUris, String basePacka private List findResourcesForUri(URI baseUri, String basePackageName, ResourceFilter resourceFilter) { List resources = new ArrayList<>(); // @formatter:off - walkFilesForUri(baseUri, ClasspathFilters.resourceFiles(), + walkFilesForUri(baseUri, SearchPathUtils::isResourceFile, (baseDir, file) -> processResourceFileSafely(baseDir, basePackageName, resourceFilter, file, resources::add)); // @formatter:on @@ -253,19 +253,6 @@ private String determineFullyQualifiedResourceName(Path baseDir, String basePack // @formatter:on } - private String determineSimpleClassName(Path file) { - String fileName = file.getFileName().toString(); - return determineSimpleClassName(fileName); - } - - static String determineSimpleClassName(String fileName) { - int lastDot = fileName.lastIndexOf('.'); - if (lastDot < 0) { - throw new JUnitException("Expected file name with file extension, but got: " + fileName); - } - return fileName.substring(0, lastDot); - } - private String determineSimpleResourceName(Path resourceFile) { return resourceFile.getFileName().toString(); } diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java index f4e5c9eaedbf..ec682e6c1132 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java @@ -24,6 +24,7 @@ import java.lang.module.ResolvedModule; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -253,11 +254,10 @@ List> scan(ModuleReference reference) { try (ModuleReader reader = reference.open()) { try (Stream names = reader.list()) { // @formatter:off - return names.filter(ClasspathFilters::isClassOrSourceFileName) - .map(DefaultClasspathScanner::determineSimpleClassName) - .map(name -> name.replace('/', '.')) - .filter(name -> !"module-info".equals(name)) - .filter(name -> !name.endsWith("package-info")) + return names.filter(name -> !name.endsWith("/")) // remove directories + .map(Path::of) + .filter(SearchPathUtils::isClassOrSourceFile) + .map(SearchPathUtils::determineFullyQualifiedClassName) .filter(classFilter::match) .> map(this::loadClassUnchecked) .filter(classFilter::match) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/SearchPathUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/SearchPathUtils.java new file mode 100644 index 000000000000..48eb38c1ed87 --- /dev/null +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/SearchPathUtils.java @@ -0,0 +1,87 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.platform.commons.util; + +import static java.util.stream.Collectors.joining; + +import java.nio.file.Path; +import java.util.stream.IntStream; + +import org.junit.platform.commons.JUnitException; + +/** + * @since 1.11 + */ +class SearchPathUtils { + + static final char PACKAGE_SEPARATOR_CHAR = '.'; + static final String PACKAGE_SEPARATOR_STRING = String.valueOf(PACKAGE_SEPARATOR_CHAR); + private static final char FILE_NAME_EXTENSION_SEPARATOR_CHAR = '.'; + + private static final String CLASS_FILE_SUFFIX = ".class"; + private static final String SOURCE_FILE_SUFFIX = ".java"; + + private static final String PACKAGE_INFO_FILE_NAME = "package-info"; + private static final String MODULE_INFO_FILE_NAME = "module-info"; + + // System property defined since Java 12: https://bugs.java/bugdatabase/JDK-8210877 + private static final boolean SOURCE_MODE = System.getProperty("jdk.launcher.sourcefile") != null; + + static boolean isResourceFile(Path file) { + return !isClassFile(file); + } + + static boolean isClassOrSourceFile(Path file) { + var fileName = file.getFileName().toString(); + return isClassOrSourceFile(fileName) && !isModuleInfoOrPackageInfo(fileName); + } + + private static boolean isModuleInfoOrPackageInfo(String fileName) { + var fileNameWithoutExtension = removeExtension(fileName); + return PACKAGE_INFO_FILE_NAME.equals(fileNameWithoutExtension) // + || MODULE_INFO_FILE_NAME.equals(fileNameWithoutExtension); + } + + static String determineFullyQualifiedClassName(Path path) { + var simpleClassName = determineSimpleClassName(path); + var parent = path.getParent(); + return parent == null ? simpleClassName : joinPathNamesWithPackageSeparator(parent.resolve(simpleClassName)); + } + + private static String joinPathNamesWithPackageSeparator(Path path) { + return IntStream.range(0, path.getNameCount()) // + .mapToObj(i -> path.getName(i).toString()) // + .collect(joining(PACKAGE_SEPARATOR_STRING)); + } + + static String determineSimpleClassName(Path file) { + return removeExtension(file.getFileName().toString()); + } + + private static String removeExtension(String fileName) { + int lastDot = fileName.lastIndexOf(FILE_NAME_EXTENSION_SEPARATOR_CHAR); + if (lastDot < 0) { + throw new JUnitException("Expected file name with file extension, but got: " + fileName); + } + return fileName.substring(0, lastDot); + } + + private static boolean isClassOrSourceFile(String name) { + return name.endsWith(CLASS_FILE_SUFFIX) || (SOURCE_MODE && name.endsWith(SOURCE_FILE_SUFFIX)); + } + + private static boolean isClassFile(Path file) { + return file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX); + } + + private SearchPathUtils() { + } +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java index 42a58f21776a..777bc2a91eef 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java @@ -103,7 +103,7 @@ void junitRunClass(@FilePrefix("junit-run-class") OutputFiles outputFiles) throw @Test @EnabledOnJre(JRE.JAVA_25) - void junitRunModule(@FilePrefix("junit-run-Module") OutputFiles outputFiles) throws Exception { + void junitRunModule(@FilePrefix("junit-run-module") OutputFiles outputFiles) throws Exception { var result = ProcessStarters.java() // .workingDir(workspace) // .putEnvironment("NO_COLOR", "1") // --disable-ansi-colors From bb3156103c6cd50384a9600051443a07508da78a Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Thu, 30 Oct 2025 13:23:11 +0100 Subject: [PATCH 16/25] Update junit-start/junit-start.gradle.kts Co-authored-by: Marc Philipp --- junit-start/junit-start.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-start/junit-start.gradle.kts b/junit-start/junit-start.gradle.kts index a86df06f0f85..8b03e5986e21 100644 --- a/junit-start/junit-start.gradle.kts +++ b/junit-start/junit-start.gradle.kts @@ -17,5 +17,5 @@ dependencies { } backwardCompatibilityChecks { - enabled = false // already checked by individual projects + enabled = false // TODO enable after initial release } From 8199ca75759f8b723ae25c2163457e5a6286fab4 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Thu, 30 Oct 2025 12:50:04 +0100 Subject: [PATCH 17/25] Remove workaround for bug fixed in JDK 9 and later --- .../platform/commons/util/DefaultClasspathScanner.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java index e19efc373bdf..849c78cd654d 100644 --- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java +++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java @@ -260,12 +260,7 @@ private String determineSimpleResourceName(Path resourceFile) { private String determineSubpackageName(Path baseDir, Path file) { Path relativePath = baseDir.relativize(file.getParent()); String pathSeparator = baseDir.getFileSystem().getSeparator(); - String subpackageName = relativePath.toString().replace(pathSeparator, PACKAGE_SEPARATOR_STRING); - if (subpackageName.endsWith(pathSeparator)) { - // TODO: Remove workaround for JDK bug: https://bugs.openjdk.org/browse/JDK-8153248 - subpackageName = subpackageName.substring(0, subpackageName.length() - pathSeparator.length()); - } - return subpackageName; + return relativePath.toString().replace(pathSeparator, PACKAGE_SEPARATOR_STRING); } private void handleInternalError(Path classFile, String fullyQualifiedClassName, InternalError ex) { From 13cabb8089a927c0d7c706a6291e2c127483285c Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 4 Nov 2025 11:45:49 +0100 Subject: [PATCH 18/25] Use Markdown syntax in API documentation --- junit-start/src/main/java/module-info.java | 34 +++++++++---------- .../src/main/java/org/junit/start/JUnit.java | 19 ++++------- .../java/org/junit/start/package-info.java | 12 +++++-- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/junit-start/src/main/java/module-info.java b/junit-start/src/main/java/module-info.java index 9917e8569067..971e4ffa2123 100644 --- a/junit-start/src/main/java/module-info.java +++ b/junit-start/src/main/java/module-info.java @@ -8,23 +8,23 @@ * https://www.eclipse.org/legal/epl-v20.html */ -/** - * Defines the API of the JUnit Start module for writing and running tests. - *

- * Usage example: - *

{@code
- * import module org.junit.start;
- *
- *  void main() {
- *    JUnit.run();
- *  }
- *
- *  @Test
- *  void addition() {
- *    Assertions.assertEquals(2, 1 + 1, "Addition error detected!");
- *  }
- * }
- */ + +/// Defines the API of the JUnit Start module for writing and running tests. +/// +/// Usage example: +/// ```java +/// import module org.junit.start; +/// +/// void main() { +/// JUnit.run(); +/// } +/// +/// @Test +/// void addition() { +/// Assertions.assertEquals(2, 1 + 1, "Addition error detected!"); +/// } +/// } +/// ``` module org.junit.start { requires static transitive org.apiguardian.api; requires static transitive org.jspecify; diff --git a/junit-start/src/main/java/org/junit/start/JUnit.java b/junit-start/src/main/java/org/junit/start/JUnit.java index 80b9e5707a3b..e170004e33b3 100644 --- a/junit-start/src/main/java/org/junit/start/JUnit.java +++ b/junit-start/src/main/java/org/junit/start/JUnit.java @@ -28,28 +28,23 @@ import org.junit.platform.launcher.core.LauncherFactory; import org.junit.platform.launcher.listeners.SummaryGeneratingListener; -@API(status = EXPERIMENTAL, since = "6.0") +/// This class provides simple helpers to discover and execute tests. +@API(status = EXPERIMENTAL, since = "6.1") public final class JUnit { - /** - * Run all tests defined in the caller class. - */ + /// Run all tests defined in the caller class. public static void run() { var walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE); run(selectClass(walker.getCallerClass())); } - /** - * Run all tests defined in the given test class. - * @param testClass the class to discover and execute tests in - */ + /// Run all tests defined in the given test class. + /// @param testClass the class to discover and execute tests in public static void run(Class testClass) { run(selectClass(testClass)); } - /** - * Run all tests defined in the given module. - * @param testModule the module to discover and execute tests in - */ + /// Run all tests defined in the given module. + /// @param testModule the module to discover and execute tests in public static void run(Module testModule) { run(selectModule(testModule)); } diff --git a/junit-start/src/main/java/org/junit/start/package-info.java b/junit-start/src/main/java/org/junit/start/package-info.java index f6b2a0ce7dda..1d7b5a9feb98 100644 --- a/junit-start/src/main/java/org/junit/start/package-info.java +++ b/junit-start/src/main/java/org/junit/start/package-info.java @@ -1,7 +1,15 @@ -/** - * Contains JUnit Start API for writing and running tests. +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html */ +/// Contains JUnit Start API for writing and running tests. + @NullMarked package org.junit.start; From 6c5ca167f0b63e40ec43585f96c29b7bc91b0c93 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 4 Nov 2025 12:18:32 +0100 Subject: [PATCH 19/25] Update User Guide, API documentation overview, and Release Notes --- .../src/docs/asciidoc/link-attributes.adoc | 4 +++ .../release-notes/release-notes-6.1.0-M1.adoc | 11 ++++++ .../asciidoc/user-guide/running-tests.adoc | 34 +++++++++++++++++++ documentation/src/javadoc/junit-overview.html | 7 ++-- 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/documentation/src/docs/asciidoc/link-attributes.adoc b/documentation/src/docs/asciidoc/link-attributes.adoc index 002e9b0585b3..6fb540e747d6 100644 --- a/documentation/src/docs/asciidoc/link-attributes.adoc +++ b/documentation/src/docs/asciidoc/link-attributes.adoc @@ -230,6 +230,8 @@ endif::[] // Jupiter Migration Support :EnableJUnit4MigrationSupport: {javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.html[@EnableJUnit4MigrationSupport] :EnableRuleMigrationSupport: {javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.html[@EnableRuleMigrationSupport] +// JUnit Start +:JUnit: {javadoc-root}/org.junit.start/org/junit/start/JUnit.html[JUnit] // Vintage :junit-vintage-engine: {javadoc-root}/org.junit.vintage.engine/org/junit/vintage/engine/package-summary.html[junit-vintage-engine] // Examples Repository @@ -247,6 +249,8 @@ endif::[] :Checkstyle: https://checkstyle.sourceforge.io[Checkstyle] :DiscussionsQA: https://github.com/junit-team/junit-framework/discussions/categories/q-a[Q&A category on GitHub Discussions] :Hamcrest: https://hamcrest.org/JavaHamcrest/[Hamcrest] +:JEP511: https://openjdk.org/jeps/511[JEP 511] +:JEP512: https://openjdk.org/jeps/512[JEP 512] :Jimfs: https://google.github.io/jimfs/[Jimfs] :Log4j: https://logging.apache.org/log4j/2.x/[Log4j] :Log4j_JDK_Logging_Adapter: https://logging.apache.org/log4j/2.x/log4j-jul/index.html[Log4j JDK Logging Adapter] diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc index bd1bce04e35d..5a4d14221df3 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc @@ -52,6 +52,17 @@ repository on GitHub. [[release-notes-6.1.0-M1-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements +* Introduce new module `org.junit.start` for writing and running tests. It simplifies + usages of JUnit in compact source files together with a single module import statement: +[source,java,indent=0] +---- +import module org.junit.start; + +void main() { JUnit.run(); } + +@Test +void test() { Assertions.assertEquals(2, 1 + 1); } +---- * Introduce new `dynamicTest(Consumer)` factory method for dynamic tests. It allows configuring the `ExecutionMode` of the dynamic test in addition to its display name, test source URI, and executable. diff --git a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc index 01cb5d23a6fa..b02c5ccd882f 100644 --- a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc @@ -836,6 +836,40 @@ DYNAMIC = 35 REPORTED = 37 ---- +[[running-tests-source-launcher]] +=== Source Launcher + +Java 25 introduced Module Import Declarations with {JEP511} and Compact Source Files +and Instance Methods with {JEP512}. The `org.junit.start` module is JUnit's "On-Ramp" +enabler, allowing to write minimal source code programs. For example, like in a +`HelloTests.java` file reading: + +```java +import module org.junit.start; + +void main() { + JUnit.run(); +} + +@Test +void stringLength() { + Assertions.assertEquals(11, "Hello JUnit".length()); +} +``` +With all required modular JAR files available in a local `lib/` directory, the +following Java 25+ command will discover and execute tests using the JUnit Platform. +It will also print the result tree to the console. + +```shell +java --module-path lib --add-modules org.junit.start HelloTests.java +╷ +└─ JUnit Jupiter ✔ + └─ HelloTests ✔ + └─ stringLength() ✔ +``` + +Find JUnit's class API documentation here: {JUnit} + [[running-tests-discovery-selectors]] === Discovery Selectors diff --git a/documentation/src/javadoc/junit-overview.html b/documentation/src/javadoc/junit-overview.html index 50837dbd73e6..5c496c12e1c7 100644 --- a/documentation/src/javadoc/junit-overview.html +++ b/documentation/src/javadoc/junit-overview.html @@ -1,6 +1,6 @@ -

This document consists of three sections:

+

This document consists of four sections:

Platform
@@ -12,13 +12,16 @@
Jupiter
JUnit Jupiter is the combination of the programming model and extension model for - writing JUnit tests and extensions. The Jupiter sub-project provides a TestEngine + writing JUnit tests and extensions. The Jupiter subproject provides a TestEngine for running Jupiter based tests on the platform.
Vintage
JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the platform.
+
Other Modules
+
This section lists all modules that are not part of a dedicated section. +

Already consulted the JUnit User Guide?

From fabc52cabb95e3f43799ef8db8c64592088ad9b3 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 7 Nov 2025 17:48:45 +0100 Subject: [PATCH 20/25] Update documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc Co-authored-by: Marc Philipp --- .../src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc index 5a4d14221df3..89e8b625bfe3 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc @@ -53,7 +53,7 @@ repository on GitHub. ==== New Features and Improvements * Introduce new module `org.junit.start` for writing and running tests. It simplifies - usages of JUnit in compact source files together with a single module import statement: + using JUnit in compact source files together with a single module import statement: [source,java,indent=0] ---- import module org.junit.start; From d15673a843900f25d1a548da423ae753536f5ff4 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 7 Nov 2025 17:56:16 +0100 Subject: [PATCH 21/25] Apply review changes --- .../release-notes/release-notes-6.1.0-M1.adoc | 11 +---------- .../tooling/support/tests/JUnitStartTests.java | 2 +- .../java/platform/tooling/support/tests/Projects.java | 2 +- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc index 89e8b625bfe3..82d90f68ac47 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc @@ -53,16 +53,7 @@ repository on GitHub. ==== New Features and Improvements * Introduce new module `org.junit.start` for writing and running tests. It simplifies - using JUnit in compact source files together with a single module import statement: -[source,java,indent=0] ----- -import module org.junit.start; - -void main() { JUnit.run(); } - -@Test -void test() { Assertions.assertEquals(2, 1 + 1); } ----- + using JUnit in compact source files together with a single module import statement. * Introduce new `dynamicTest(Consumer)` factory method for dynamic tests. It allows configuring the `ExecutionMode` of the dynamic test in addition to its display name, test source URI, and executable. diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java index 777bc2a91eef..95d62c2b3050 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java @@ -39,7 +39,7 @@ class JUnitStartTests { @BeforeAll static void prepareLocalLibraryDirectoryWithJUnitModules() throws Exception { - copyToWorkspace(Projects.JUNIT_start, workspace); + copyToWorkspace(Projects.JUNIT_START, workspace); var lib = workspace.resolve("lib"); try { Files.createDirectories(lib); diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java index b62b9ca99cf4..79e28dc15388 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java @@ -21,7 +21,7 @@ public class Projects { public static final String GRADLE_KOTLIN_EXTENSIONS = "gradle-kotlin-extensions"; public static final String GRADLE_MISSING_ENGINE = "gradle-missing-engine"; public static final String JAR_DESCRIBE_MODULE = "jar-describe-module"; - public static final String JUNIT_start = "junit-start"; + public static final String JUNIT_START = "junit-start"; public static final String JUPITER_STARTER = "jupiter-starter"; public static final String KOTLIN_COROUTINES = "kotlin-coroutines"; public static final String MAVEN_SUREFIRE_COMPATIBILITY = "maven-surefire-compatibility"; From 3ff5a4ea73138c63b76ceec8340fc9632072d452 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Fri, 7 Nov 2025 17:56:47 +0100 Subject: [PATCH 22/25] Remove source snippet from release note --- .../src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc index 82d90f68ac47..1543c4fda988 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M1.adoc @@ -53,7 +53,7 @@ repository on GitHub. ==== New Features and Improvements * Introduce new module `org.junit.start` for writing and running tests. It simplifies - using JUnit in compact source files together with a single module import statement. + using JUnit in compact source files together with a single module import statement: * Introduce new `dynamicTest(Consumer)` factory method for dynamic tests. It allows configuring the `ExecutionMode` of the dynamic test in addition to its display name, test source URI, and executable. From 49bf8eaeed8cd22fe8c94298090b1e39e0cb2b13 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Sat, 8 Nov 2025 17:16:03 +0100 Subject: [PATCH 23/25] Add `JUnitStartTests.java` to documentation project --- documentation/documentation.gradle.kts | 9 ++++++++- .../src/test/java/JUnitStartTests.java | 20 +++++++++++++++++++ .../support/tests/ModularUserGuideTests.java | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 documentation/src/test/java/JUnitStartTests.java diff --git a/documentation/documentation.gradle.kts b/documentation/documentation.gradle.kts index 5acf6fd77cb1..92810adb1afb 100644 --- a/documentation/documentation.gradle.kts +++ b/documentation/documentation.gradle.kts @@ -28,7 +28,13 @@ modularProjects.forEach { evaluationDependsOn(it.path) } javaLibrary { mainJavaVersion = JavaVersion.VERSION_17 - testJavaVersion = JavaVersion.VERSION_17 + testJavaVersion = JavaVersion.VERSION_25 +} + +spotless { + java { + targetExclude("src/test/java/JUnitStartTests.java") // due to compact source files and module imports + } } val apiReport = configurations.dependencyScope("apiReport") @@ -68,6 +74,7 @@ dependencies { testImplementation(projects.junitPlatformConsole) testImplementation(projects.junitPlatformSuite) testImplementation(projects.junitPlatformTestkit) + testImplementation(projects.junitStart) testImplementation(projects.junitVintageEngine) testImplementation(kotlin("stdlib")) testRuntimeOnly(libs.kotlinx.coroutines) diff --git a/documentation/src/test/java/JUnitStartTests.java b/documentation/src/test/java/JUnitStartTests.java new file mode 100644 index 000000000000..27a5690542af --- /dev/null +++ b/documentation/src/test/java/JUnitStartTests.java @@ -0,0 +1,20 @@ + +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ +import module org.junit.start; + +void main() { + JUnit.run(); +} + +@Test +void addition() { + Assertions.assertEquals(2, 1 + 1); +} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java index daccabb3f49b..cf7106a30df5 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java @@ -48,6 +48,7 @@ class ModularUserGuideTests { open module documentation { exports example.testkit; // just here to ensure documentation example sources are compiled + requires org.junit.start; requires org.junit.jupiter.api; requires org.junit.jupiter.migrationsupport; requires org.junit.jupiter.params; @@ -102,6 +103,8 @@ private static List compile(Path temp, Writer out, Writer err) throws Ex .filter(s -> s.endsWith(".java")) // // TypeError: systemProperty.get is not a function ?!?! .filter(s -> !s.endsWith("ConditionalTestExecutionDemo.java")) // + // InvalidModuleDescriptorException: Unnamed package not allowed in module + .filter(s -> !s.endsWith("JUnitStartTests.java")) // // Don't include command-line tools that "require io.github.classgraph" .filter(s -> !s.contains("tools")).forEach(args::add); } From f912b00ffbc4e0b7f6c6f0252df173f50062ba64 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Sun, 9 Nov 2025 09:46:42 +0100 Subject: [PATCH 24/25] Revert "Add `JUnitStartTests.java` to documentation project" This reverts commit 49bf8eaeed8cd22fe8c94298090b1e39e0cb2b13. --- documentation/documentation.gradle.kts | 9 +-------- .../src/test/java/JUnitStartTests.java | 20 ------------------- .../support/tests/ModularUserGuideTests.java | 3 --- 3 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 documentation/src/test/java/JUnitStartTests.java diff --git a/documentation/documentation.gradle.kts b/documentation/documentation.gradle.kts index 92810adb1afb..5acf6fd77cb1 100644 --- a/documentation/documentation.gradle.kts +++ b/documentation/documentation.gradle.kts @@ -28,13 +28,7 @@ modularProjects.forEach { evaluationDependsOn(it.path) } javaLibrary { mainJavaVersion = JavaVersion.VERSION_17 - testJavaVersion = JavaVersion.VERSION_25 -} - -spotless { - java { - targetExclude("src/test/java/JUnitStartTests.java") // due to compact source files and module imports - } + testJavaVersion = JavaVersion.VERSION_17 } val apiReport = configurations.dependencyScope("apiReport") @@ -74,7 +68,6 @@ dependencies { testImplementation(projects.junitPlatformConsole) testImplementation(projects.junitPlatformSuite) testImplementation(projects.junitPlatformTestkit) - testImplementation(projects.junitStart) testImplementation(projects.junitVintageEngine) testImplementation(kotlin("stdlib")) testRuntimeOnly(libs.kotlinx.coroutines) diff --git a/documentation/src/test/java/JUnitStartTests.java b/documentation/src/test/java/JUnitStartTests.java deleted file mode 100644 index 27a5690542af..000000000000 --- a/documentation/src/test/java/JUnitStartTests.java +++ /dev/null @@ -1,20 +0,0 @@ - -/* - * Copyright 2015-2025 the original author or authors. - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License v2.0 which - * accompanies this distribution and is available at - * - * https://www.eclipse.org/legal/epl-v20.html - */ -import module org.junit.start; - -void main() { - JUnit.run(); -} - -@Test -void addition() { - Assertions.assertEquals(2, 1 + 1); -} diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java index cf7106a30df5..daccabb3f49b 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java @@ -48,7 +48,6 @@ class ModularUserGuideTests { open module documentation { exports example.testkit; // just here to ensure documentation example sources are compiled - requires org.junit.start; requires org.junit.jupiter.api; requires org.junit.jupiter.migrationsupport; requires org.junit.jupiter.params; @@ -103,8 +102,6 @@ private static List compile(Path temp, Writer out, Writer err) throws Ex .filter(s -> s.endsWith(".java")) // // TypeError: systemProperty.get is not a function ?!?! .filter(s -> !s.endsWith("ConditionalTestExecutionDemo.java")) // - // InvalidModuleDescriptorException: Unnamed package not allowed in module - .filter(s -> !s.endsWith("JUnitStartTests.java")) // // Don't include command-line tools that "require io.github.classgraph" .filter(s -> !s.contains("tools")).forEach(args::add); } From fc5e9549781e314a113dc4394aa4d3c3f59d6d7c Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Sun, 9 Nov 2025 10:03:22 +0100 Subject: [PATCH 25/25] Reword "Source Launcher" introduction --- documentation/src/docs/asciidoc/link-attributes.adoc | 2 -- .../src/docs/asciidoc/user-guide/running-tests.adoc | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/documentation/src/docs/asciidoc/link-attributes.adoc b/documentation/src/docs/asciidoc/link-attributes.adoc index 6fb540e747d6..ce4f227cd13e 100644 --- a/documentation/src/docs/asciidoc/link-attributes.adoc +++ b/documentation/src/docs/asciidoc/link-attributes.adoc @@ -249,8 +249,6 @@ endif::[] :Checkstyle: https://checkstyle.sourceforge.io[Checkstyle] :DiscussionsQA: https://github.com/junit-team/junit-framework/discussions/categories/q-a[Q&A category on GitHub Discussions] :Hamcrest: https://hamcrest.org/JavaHamcrest/[Hamcrest] -:JEP511: https://openjdk.org/jeps/511[JEP 511] -:JEP512: https://openjdk.org/jeps/512[JEP 512] :Jimfs: https://google.github.io/jimfs/[Jimfs] :Log4j: https://logging.apache.org/log4j/2.x/[Log4j] :Log4j_JDK_Logging_Adapter: https://logging.apache.org/log4j/2.x/log4j-jul/index.html[Log4j JDK Logging Adapter] diff --git a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc index b02c5ccd882f..0efc30678595 100644 --- a/documentation/src/docs/asciidoc/user-guide/running-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/running-tests.adoc @@ -839,10 +839,9 @@ REPORTED = 37 [[running-tests-source-launcher]] === Source Launcher -Java 25 introduced Module Import Declarations with {JEP511} and Compact Source Files -and Instance Methods with {JEP512}. The `org.junit.start` module is JUnit's "On-Ramp" -enabler, allowing to write minimal source code programs. For example, like in a -`HelloTests.java` file reading: +Starting with Java 25 it is possible to write minimal source code test programs +using the `org.junit.start` module. For example, like in a `HelloTests.java` +file reading: ```java import module org.junit.start;