From e0a5c721cb0a1edde85f5860b15eadcc2a9dee5f Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 11 Nov 2025 11:40:36 +0100 Subject: [PATCH 01/11] Add example for using Java's source launcher --- junit-source-launcher/.gitignore | 1 + junit-source-launcher/README.md | 38 ++++++++++ .../init/DownloadRequiredModules.java | 74 +++++++++++++++++++ .../init/module-uri.properties | 17 +++++ junit-source-launcher/src/HelloTests.java | 20 +++++ src/Builder.java | 11 ++- 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 junit-source-launcher/.gitignore create mode 100644 junit-source-launcher/README.md create mode 100644 junit-source-launcher/init/DownloadRequiredModules.java create mode 100644 junit-source-launcher/init/module-uri.properties create mode 100644 junit-source-launcher/src/HelloTests.java diff --git a/junit-source-launcher/.gitignore b/junit-source-launcher/.gitignore new file mode 100644 index 00000000..c3af8579 --- /dev/null +++ b/junit-source-launcher/.gitignore @@ -0,0 +1 @@ +lib/ diff --git a/junit-source-launcher/README.md b/junit-source-launcher/README.md new file mode 100644 index 00000000..07981c6a --- /dev/null +++ b/junit-source-launcher/README.md @@ -0,0 +1,38 @@ +# junit-source-launcher + +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](src/HelloTests.java) file reading: + +```java +import module org.junit.start; + +void main() { + JUnit.run(); +} + +@Test +void stringLength() { + Assertions.assertEquals(11, "Hello JUnit".length()); +} +``` + +Download `org.junit.start` module and its transitively required modules into a local `lib/` directory by running in a shell: + +```shell +java init/DownloadRequiredModules.java +``` + +With all required modular JAR files available in a local `lib/` directory, the following Java command will discover and execute tests using the JUnit Platform. + +```shell +java --module-path lib --add-modules org.junit.start src/HelloTests.java +``` + +It will also print the result tree to the console. + +```text +╷ +└─ JUnit Jupiter ✔ + └─ HelloTests ✔ + └─ stringLength() ✔ +``` diff --git a/junit-source-launcher/init/DownloadRequiredModules.java b/junit-source-launcher/init/DownloadRequiredModules.java new file mode 100644 index 00000000..bc5fa84e --- /dev/null +++ b/junit-source-launcher/init/DownloadRequiredModules.java @@ -0,0 +1,74 @@ +/* + * 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 static java.lang.module.ModuleDescriptor.Requires.Modifier.STATIC; + +void main() throws Exception { + var program = Path.of("src", "HelloTests.java"); + if (!Files.exists(program)) { + throw new AssertionError("Expected %s in current working directory".formatted(program)); + } + var properties = new Properties(); + properties.load(new FileReader("init/module-uri.properties")); + var lib = Files.createDirectories(Path.of("lib")); + downloadModules(lib, properties, Set.of("org.junit.start", "org.apiguardian.api")); + var missing = computeMissingModuleNames(lib); + while (!missing.isEmpty()) { + downloadModules(lib, properties, missing); + missing = computeMissingModuleNames(lib); + } + System.out.printf("%nList modules of %s directory%n", lib); + listModules(lib); +} + +static void downloadModules(Path directory, Properties properties, Set names) throws Exception { + for (var name : names) { + var target = directory.resolve(name + ".jar"); + if (Files.exists(target)) continue; + var source = URI.create(properties.getProperty(name)); + try (var stream = source.toURL().openStream()) { + System.out.println(name + " < " + source + "..."); + Files.copy(stream, target); + } + } + var finder = ModuleFinder.of(directory); + var remainder = new TreeSet<>(names); + remainder.removeIf(name -> finder.find(name).isPresent()); + if (remainder.isEmpty()) return; + throw new AssertionError("Modules not downloaded: " + remainder); +} + +static Set computeMissingModuleNames(Path directory) { + var system = ModuleFinder.ofSystem(); + var finder = ModuleFinder.of(directory); + var names = + finder.findAll().stream() + .parallel() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::requires) + .flatMap(Collection::stream) + .filter(requires -> !requires.modifiers().contains(STATIC)) + .map(ModuleDescriptor.Requires::name) + .filter(name -> finder.find(name).isEmpty()) + .filter(name -> system.find(name).isEmpty()) + .toList(); + return new TreeSet<>(names); +} + +static void listModules(Path directory) { + var finder = ModuleFinder.of(directory); + var modules = finder.findAll(); + modules.stream() + .map(ModuleReference::descriptor) + .map(ModuleDescriptor::toNameAndVersion) + .sorted() + .forEach(System.out::println); + System.out.printf(" %d modules%n", modules.size()); +} diff --git a/junit-source-launcher/init/module-uri.properties b/junit-source-launcher/init/module-uri.properties new file mode 100644 index 00000000..b482a38d --- /dev/null +++ b/junit-source-launcher/init/module-uri.properties @@ -0,0 +1,17 @@ +org.apiguardian.api=https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar +org.jspecify=https://repo.maven.apache.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar +org.junit.jupiter.api=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter-api/6.1.0-SNAPSHOT/junit-jupiter-api-6.1.0-20251110.124544-116.jar +org.junit.jupiter.engine=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter-engine/6.1.0-SNAPSHOT/junit-jupiter-engine-6.1.0-20251110.124544-117.jar +org.junit.jupiter.params=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter-params/6.1.0-SNAPSHOT/junit-jupiter-params-6.1.0-20251110.124544-116.jar +org.junit.jupiter=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter/6.1.0-SNAPSHOT/junit-jupiter-6.1.0-20251110.124544-117.jar +org.junit.platform.commons=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-commons/6.1.0-SNAPSHOT/junit-platform-commons-6.1.0-20251110.124544-116.jar +org.junit.platform.console=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-console/6.1.0-SNAPSHOT/junit-platform-console-6.1.0-20251110.124544-116.jar +org.junit.platform.engine=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-engine/6.1.0-SNAPSHOT/junit-platform-engine-6.1.0-20251110.124544-116.jar +org.junit.platform.launcher=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-launcher/6.1.0-SNAPSHOT/junit-platform-launcher-6.1.0-20251110.124544-116.jar +org.junit.platform.reporting=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-reporting/6.1.0-SNAPSHOT/junit-platform-reporting-6.1.0-20251110.124544-116.jar +org.junit.platform.suite.api=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-suite-api/6.1.0-SNAPSHOT/junit-platform-suite-api-6.1.0-20251110.124544-116.jar +org.junit.platform.suite.engine=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-suite-engine/6.1.0-SNAPSHOT/junit-platform-suite-engine-6.1.0-20251110.124544-116.jar +org.junit.platform.suite=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-suite/6.1.0-SNAPSHOT/junit-platform-suite-6.1.0-20251110.124544-116.jar +org.junit.start=https://central.sonatype.com/repository/maven-snapshots/org/junit/junit-start/6.1.0-SNAPSHOT/junit-start-6.1.0-20251110.124544-1.jar +org.opentest4j.reporting.tooling.spi=https://repo.maven.apache.org/maven2/org/opentest4j/reporting/open-test-reporting-tooling-spi/0.2.5/open-test-reporting-tooling-spi-0.2.5.jar +org.opentest4j=https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar diff --git a/junit-source-launcher/src/HelloTests.java b/junit-source-launcher/src/HelloTests.java new file mode 100644 index 00000000..f1c389c5 --- /dev/null +++ b/junit-source-launcher/src/HelloTests.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 stringLength() { + Assertions.assertEquals(11, "Hello JUnit".length()); +} diff --git a/src/Builder.java b/src/Builder.java index d05c55a0..b7c310ee 100644 --- a/src/Builder.java +++ b/src/Builder.java @@ -77,6 +77,15 @@ int build(Target target, Set excludedProjects) { // modular runProject(excludedProjects, "junit-modular-world", "java", modularAction); + // source launcher + /* + runProject(excludedProjects, "junit-source-launcher", "java", "init/DownloadRequiredModules.java"); + runProject(excludedProjects, "junit-source-launcher", + "java", + "--module-path", "lib", + "--add-modules", "org.junit.start", + "src/HelloTests.java"); + */ System.out.printf("%n%n%n|%n| Done. Build exits with status = %d.%n|%n", status); return status; } @@ -141,7 +150,7 @@ void run(String directory, String executable, String... args) { } boolean isWindows() { - return System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win"); + return System.getProperty("os.name").toLowerCase(Locale.ROOT).startsWith("win"); } void checkLicense(String blueprint, String... extensions) { From a5eb039411d3fe0bc7e6fe1d17b401939ed6a6f5 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 11 Nov 2025 13:43:37 +0100 Subject: [PATCH 02/11] Enable run for `junit-source-launcher` --- src/Builder.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Builder.java b/src/Builder.java index b7c310ee..6734f306 100644 --- a/src/Builder.java +++ b/src/Builder.java @@ -78,14 +78,12 @@ int build(Target target, Set excludedProjects) { runProject(excludedProjects, "junit-modular-world", "java", modularAction); // source launcher - /* runProject(excludedProjects, "junit-source-launcher", "java", "init/DownloadRequiredModules.java"); runProject(excludedProjects, "junit-source-launcher", "java", "--module-path", "lib", "--add-modules", "org.junit.start", "src/HelloTests.java"); - */ System.out.printf("%n%n%n|%n| Done. Build exits with status = %d.%n|%n", status); return status; } From 62e0a4c4b7cea5f6e953b4a755d4893bdc46d94a Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 11 Nov 2025 14:13:38 +0100 Subject: [PATCH 03/11] Download JAR files in parallel --- .../init/DownloadRequiredModules.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/junit-source-launcher/init/DownloadRequiredModules.java b/junit-source-launcher/init/DownloadRequiredModules.java index bc5fa84e..6558da35 100644 --- a/junit-source-launcher/init/DownloadRequiredModules.java +++ b/junit-source-launcher/init/DownloadRequiredModules.java @@ -18,26 +18,29 @@ void main() throws Exception { var properties = new Properties(); properties.load(new FileReader("init/module-uri.properties")); var lib = Files.createDirectories(Path.of("lib")); - downloadModules(lib, properties, Set.of("org.junit.start", "org.apiguardian.api")); + downloadModules(Set.of("org.junit.start", "org.apiguardian.api"), lib, properties); var missing = computeMissingModuleNames(lib); while (!missing.isEmpty()) { - downloadModules(lib, properties, missing); + downloadModules(missing, lib, properties); missing = computeMissingModuleNames(lib); } - System.out.printf("%nList modules of %s directory%n", lib); + IO.println("%nList modules of %s directory".formatted(lib)); listModules(lib); } -static void downloadModules(Path directory, Properties properties, Set names) throws Exception { - for (var name : names) { - var target = directory.resolve(name + ".jar"); - if (Files.exists(target)) continue; - var source = URI.create(properties.getProperty(name)); - try (var stream = source.toURL().openStream()) { - System.out.println(name + " < " + source + "..."); - Files.copy(stream, target); - } - } +static void downloadModules(Set names, Path directory, Properties properties) { + IO.println("Downloading %d module%s".formatted(names.size(), names.size() == 1 ? "" : "s")); + names.stream().parallel().forEach(name -> { + var target = directory.resolve(name + ".jar"); + if (Files.exists(target)) return; + var source = URI.create(properties.getProperty(name)); + try (var stream = source.toURL().openStream()) { + IO.println(name + " <- " + source + "..."); + Files.copy(stream, target); + } catch (IOException cause) { + throw new UncheckedIOException(cause); + } + }); var finder = ModuleFinder.of(directory); var remainder = new TreeSet<>(names); remainder.removeIf(name -> finder.find(name).isPresent()); @@ -69,6 +72,6 @@ static void listModules(Path directory) { .map(ModuleReference::descriptor) .map(ModuleDescriptor::toNameAndVersion) .sorted() - .forEach(System.out::println); - System.out.printf(" %d modules%n", modules.size()); + .forEach(IO::println); + IO.println(" %d modules".formatted(modules.size())); } From 3b7e4a0bac16eb4f4e15f501bd2ea1e4157eebb5 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Tue, 11 Nov 2025 18:27:20 +0100 Subject: [PATCH 04/11] Add comments and extract constants --- .../init/DownloadRequiredModules.java | 48 ++++++++++++------- junit-source-launcher/src/HelloTests.java | 2 +- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/junit-source-launcher/init/DownloadRequiredModules.java b/junit-source-launcher/init/DownloadRequiredModules.java index 6558da35..449ab6be 100644 --- a/junit-source-launcher/init/DownloadRequiredModules.java +++ b/junit-source-launcher/init/DownloadRequiredModules.java @@ -8,31 +8,40 @@ * https://www.eclipse.org/legal/epl-v20.html */ -import static java.lang.module.ModuleDescriptor.Requires.Modifier.STATIC; +final Path lib = Path.of("lib"); +final Set roots = Set.of("org.junit.start"); void main() throws Exception { + // Ensure being launched inside expected working directory var program = Path.of("src", "HelloTests.java"); if (!Files.exists(program)) { throw new AssertionError("Expected %s in current working directory".formatted(program)); } + + // Read mapping file to locate remote modules var properties = new Properties(); properties.load(new FileReader("init/module-uri.properties")); - var lib = Files.createDirectories(Path.of("lib")); - downloadModules(Set.of("org.junit.start", "org.apiguardian.api"), lib, properties); - var missing = computeMissingModuleNames(lib); + + // Create and initialize lib directory with root module(s) + Files.createDirectories(lib); + downloadModules(roots, properties); + + // Compute missing modules and download them transitively + var missing = computeMissingModuleNames(); while (!missing.isEmpty()) { - downloadModules(missing, lib, properties); - missing = computeMissingModuleNames(lib); + downloadModules(missing, properties); + missing = computeMissingModuleNames(); } + IO.println("%nList modules of %s directory".formatted(lib)); - listModules(lib); + listModules(); } -static void downloadModules(Set names, Path directory, Properties properties) { +void downloadModules(Set names, Properties properties) { IO.println("Downloading %d module%s".formatted(names.size(), names.size() == 1 ? "" : "s")); names.stream().parallel().forEach(name -> { - var target = directory.resolve(name + ".jar"); - if (Files.exists(target)) return; + var target = lib.resolve(name + ".jar"); + if (Files.exists(target)) return; // Don't overwrite existing JAR file var source = URI.create(properties.getProperty(name)); try (var stream = source.toURL().openStream()) { IO.println(name + " <- " + source + "..."); @@ -41,23 +50,24 @@ static void downloadModules(Set names, Path directory, Properties proper throw new UncheckedIOException(cause); } }); - var finder = ModuleFinder.of(directory); + // Ensure that every name can be found to avoid eternal loops + var finder = ModuleFinder.of(lib); var remainder = new TreeSet<>(names); remainder.removeIf(name -> finder.find(name).isPresent()); if (remainder.isEmpty()) return; throw new AssertionError("Modules not downloaded: " + remainder); } -static Set computeMissingModuleNames(Path directory) { +Set computeMissingModuleNames() { var system = ModuleFinder.ofSystem(); - var finder = ModuleFinder.of(directory); + var finder = ModuleFinder.of(lib); var names = finder.findAll().stream() .parallel() .map(ModuleReference::descriptor) .map(ModuleDescriptor::requires) .flatMap(Collection::stream) - .filter(requires -> !requires.modifiers().contains(STATIC)) + .filter(this::mustBePresentAtCompileTime) .map(ModuleDescriptor.Requires::name) .filter(name -> finder.find(name).isEmpty()) .filter(name -> system.find(name).isEmpty()) @@ -65,8 +75,14 @@ static Set computeMissingModuleNames(Path directory) { return new TreeSet<>(names); } -static void listModules(Path directory) { - var finder = ModuleFinder.of(directory); +boolean mustBePresentAtCompileTime(ModuleDescriptor.Requires requires) { + var isStatic = requires.modifiers().contains(ModuleDescriptor.Requires.Modifier.STATIC); + var isTransitive = requires.modifiers().contains(ModuleDescriptor.Requires.Modifier.TRANSITIVE); + return !isStatic || isTransitive; +} + +void listModules() { + var finder = ModuleFinder.of(lib); var modules = finder.findAll(); modules.stream() .map(ModuleReference::descriptor) diff --git a/junit-source-launcher/src/HelloTests.java b/junit-source-launcher/src/HelloTests.java index f1c389c5..3eaef954 100644 --- a/junit-source-launcher/src/HelloTests.java +++ b/junit-source-launcher/src/HelloTests.java @@ -16,5 +16,5 @@ void main() { @Test void stringLength() { - Assertions.assertEquals(11, "Hello JUnit".length()); + Assertions.assertEquals(11, "Hello JUnit".length()); } From 4ede9820af41d39eb40bd57760ed264abed9e37f Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 19 Nov 2025 08:32:56 +0100 Subject: [PATCH 05/11] Inline module lookup file --- junit-source-launcher/.gitignore | 4 +++- junit-source-launcher/README.md | 4 ++-- .../init/module-uri.properties | 17 -------------- .../DownloadRequiredModules.java | 23 ++++++++++++++++++- src/Builder.java | 2 +- 5 files changed, 28 insertions(+), 22 deletions(-) delete mode 100644 junit-source-launcher/init/module-uri.properties rename junit-source-launcher/{init => lib}/DownloadRequiredModules.java (55%) diff --git a/junit-source-launcher/.gitignore b/junit-source-launcher/.gitignore index c3af8579..dc6bf831 100644 --- a/junit-source-launcher/.gitignore +++ b/junit-source-launcher/.gitignore @@ -1 +1,3 @@ -lib/ +out/ + +*.jar diff --git a/junit-source-launcher/README.md b/junit-source-launcher/README.md index 07981c6a..ebec5028 100644 --- a/junit-source-launcher/README.md +++ b/junit-source-launcher/README.md @@ -1,7 +1,7 @@ # junit-source-launcher 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](src/HelloTests.java) file reading: +For example, take a look at the [HelloTests.java](src/HelloTests.java) file reading: ```java import module org.junit.start; @@ -19,7 +19,7 @@ void stringLength() { Download `org.junit.start` module and its transitively required modules into a local `lib/` directory by running in a shell: ```shell -java init/DownloadRequiredModules.java +java lib/DownloadRequiredModules.java ``` With all required modular JAR files available in a local `lib/` directory, the following Java command will discover and execute tests using the JUnit Platform. diff --git a/junit-source-launcher/init/module-uri.properties b/junit-source-launcher/init/module-uri.properties deleted file mode 100644 index b482a38d..00000000 --- a/junit-source-launcher/init/module-uri.properties +++ /dev/null @@ -1,17 +0,0 @@ -org.apiguardian.api=https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar -org.jspecify=https://repo.maven.apache.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar -org.junit.jupiter.api=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter-api/6.1.0-SNAPSHOT/junit-jupiter-api-6.1.0-20251110.124544-116.jar -org.junit.jupiter.engine=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter-engine/6.1.0-SNAPSHOT/junit-jupiter-engine-6.1.0-20251110.124544-117.jar -org.junit.jupiter.params=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter-params/6.1.0-SNAPSHOT/junit-jupiter-params-6.1.0-20251110.124544-116.jar -org.junit.jupiter=https://central.sonatype.com/repository/maven-snapshots/org/junit/jupiter/junit-jupiter/6.1.0-SNAPSHOT/junit-jupiter-6.1.0-20251110.124544-117.jar -org.junit.platform.commons=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-commons/6.1.0-SNAPSHOT/junit-platform-commons-6.1.0-20251110.124544-116.jar -org.junit.platform.console=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-console/6.1.0-SNAPSHOT/junit-platform-console-6.1.0-20251110.124544-116.jar -org.junit.platform.engine=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-engine/6.1.0-SNAPSHOT/junit-platform-engine-6.1.0-20251110.124544-116.jar -org.junit.platform.launcher=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-launcher/6.1.0-SNAPSHOT/junit-platform-launcher-6.1.0-20251110.124544-116.jar -org.junit.platform.reporting=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-reporting/6.1.0-SNAPSHOT/junit-platform-reporting-6.1.0-20251110.124544-116.jar -org.junit.platform.suite.api=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-suite-api/6.1.0-SNAPSHOT/junit-platform-suite-api-6.1.0-20251110.124544-116.jar -org.junit.platform.suite.engine=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-suite-engine/6.1.0-SNAPSHOT/junit-platform-suite-engine-6.1.0-20251110.124544-116.jar -org.junit.platform.suite=https://central.sonatype.com/repository/maven-snapshots/org/junit/platform/junit-platform-suite/6.1.0-SNAPSHOT/junit-platform-suite-6.1.0-20251110.124544-116.jar -org.junit.start=https://central.sonatype.com/repository/maven-snapshots/org/junit/junit-start/6.1.0-SNAPSHOT/junit-start-6.1.0-20251110.124544-1.jar -org.opentest4j.reporting.tooling.spi=https://repo.maven.apache.org/maven2/org/opentest4j/reporting/open-test-reporting-tooling-spi/0.2.5/open-test-reporting-tooling-spi-0.2.5.jar -org.opentest4j=https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar diff --git a/junit-source-launcher/init/DownloadRequiredModules.java b/junit-source-launcher/lib/DownloadRequiredModules.java similarity index 55% rename from junit-source-launcher/init/DownloadRequiredModules.java rename to junit-source-launcher/lib/DownloadRequiredModules.java index 449ab6be..a601a73a 100644 --- a/junit-source-launcher/init/DownloadRequiredModules.java +++ b/junit-source-launcher/lib/DownloadRequiredModules.java @@ -10,6 +10,27 @@ final Path lib = Path.of("lib"); final Set roots = Set.of("org.junit.start"); +final String lookup = + //language=Properties + """ + org.apiguardian.api=https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar + org.jspecify=https://repo.maven.apache.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar + org.junit.jupiter.api=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-api/6.1.0-M1/junit-jupiter-api-6.1.0-M1.jar + org.junit.jupiter.engine=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-engine/6.1.0-M1/junit-jupiter-engine-6.1.0-M1.jar + org.junit.jupiter.params=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-params/6.1.0-M1/junit-jupiter-params-6.1.0-M1.jar + org.junit.jupiter=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter/6.1.0-M1/junit-jupiter-6.1.0-M1.jar + org.junit.platform.commons=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/6.1.0-M1/junit-platform-commons-6.1.0-M1.jar + org.junit.platform.console=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-console/6.1.0-M1/junit-platform-console-6.1.0-M1.jar + org.junit.platform.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-engine/6.1.0-M1/junit-platform-engine-6.1.0-M1.jar + org.junit.platform.launcher=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-launcher/6.1.0-M1/junit-platform-launcher-6.1.0-M1.jar + org.junit.platform.reporting=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-reporting/6.1.0-M1/junit-platform-reporting-6.1.0-M1.jar + org.junit.platform.suite.api=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-api/6.1.0-M1/junit-platform-suite-api-6.1.0-M1.jar + org.junit.platform.suite.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-engine/6.1.0-M1/junit-platform-suite-engine-6.1.0-M1.jar + org.junit.platform.suite=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite/6.1.0-M1/junit-platform-suite-6.1.0-M1.jar + org.junit.start=https://repo.maven.apache.org/maven2/org/junit/junit-start/6.1.0-M1/junit-start-6.1.0-M1.jar + org.opentest4j.reporting.tooling.spi=https://repo.maven.apache.org/maven2/org/opentest4j/reporting/open-test-reporting-tooling-spi/0.2.5/open-test-reporting-tooling-spi-0.2.5.jar + org.opentest4j=https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar + """; void main() throws Exception { // Ensure being launched inside expected working directory @@ -20,7 +41,7 @@ void main() throws Exception { // Read mapping file to locate remote modules var properties = new Properties(); - properties.load(new FileReader("init/module-uri.properties")); + properties.load(new StringReader(lookup)); // Create and initialize lib directory with root module(s) Files.createDirectories(lib); diff --git a/src/Builder.java b/src/Builder.java index 6734f306..14f0b7cc 100644 --- a/src/Builder.java +++ b/src/Builder.java @@ -78,7 +78,7 @@ int build(Target target, Set excludedProjects) { runProject(excludedProjects, "junit-modular-world", "java", modularAction); // source launcher - runProject(excludedProjects, "junit-source-launcher", "java", "init/DownloadRequiredModules.java"); + runProject(excludedProjects, "junit-source-launcher", "java", "lib/DownloadRequiredModules.java"); runProject(excludedProjects, "junit-source-launcher", "java", "--module-path", "lib", From 742cb1c5c2cdfa11088aaa6bdd4ee545f1a69cf5 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 19 Nov 2025 08:33:38 +0100 Subject: [PATCH 06/11] Add "empty" JUnit configuration file --- junit-source-launcher/src/junit-platform.properties | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 junit-source-launcher/src/junit-platform.properties diff --git a/junit-source-launcher/src/junit-platform.properties b/junit-source-launcher/src/junit-platform.properties new file mode 100644 index 00000000..731f4d5c --- /dev/null +++ b/junit-source-launcher/src/junit-platform.properties @@ -0,0 +1,6 @@ +## +## Enable JUnit Platform Reporting +## -> https://docs.junit.org/current/user-guide/#junit-platform-reporting +## +# junit.platform.reporting.output.dir=out/junit-{uniqueNumber} +# junit.platform.reporting.open.xml.enabled=true From ba285894bfcbf3ce1a21c7f9c190adf3f069d018 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 19 Nov 2025 09:07:13 +0100 Subject: [PATCH 07/11] Update `Updater` --- .../lib/DownloadRequiredModules.java | 33 ++++++++++--------- src/Updater.java | 7 +++- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/junit-source-launcher/lib/DownloadRequiredModules.java b/junit-source-launcher/lib/DownloadRequiredModules.java index a601a73a..af0ac8ec 100644 --- a/junit-source-launcher/lib/DownloadRequiredModules.java +++ b/junit-source-launcher/lib/DownloadRequiredModules.java @@ -8,29 +8,30 @@ * https://www.eclipse.org/legal/epl-v20.html */ -final Path lib = Path.of("lib"); -final Set roots = Set.of("org.junit.start"); +final Path lib = Path.of("lib"); // local directory to be used in module path +final Set roots = Set.of("org.junit.start"); // single root module to lookup +final String version = "6.1.0-M1"; // of JUnit Platform final String lookup = //language=Properties """ org.apiguardian.api=https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar org.jspecify=https://repo.maven.apache.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar - org.junit.jupiter.api=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-api/6.1.0-M1/junit-jupiter-api-6.1.0-M1.jar - org.junit.jupiter.engine=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-engine/6.1.0-M1/junit-jupiter-engine-6.1.0-M1.jar - org.junit.jupiter.params=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-params/6.1.0-M1/junit-jupiter-params-6.1.0-M1.jar - org.junit.jupiter=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter/6.1.0-M1/junit-jupiter-6.1.0-M1.jar - org.junit.platform.commons=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/6.1.0-M1/junit-platform-commons-6.1.0-M1.jar - org.junit.platform.console=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-console/6.1.0-M1/junit-platform-console-6.1.0-M1.jar - org.junit.platform.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-engine/6.1.0-M1/junit-platform-engine-6.1.0-M1.jar - org.junit.platform.launcher=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-launcher/6.1.0-M1/junit-platform-launcher-6.1.0-M1.jar - org.junit.platform.reporting=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-reporting/6.1.0-M1/junit-platform-reporting-6.1.0-M1.jar - org.junit.platform.suite.api=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-api/6.1.0-M1/junit-platform-suite-api-6.1.0-M1.jar - org.junit.platform.suite.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-engine/6.1.0-M1/junit-platform-suite-engine-6.1.0-M1.jar - org.junit.platform.suite=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite/6.1.0-M1/junit-platform-suite-6.1.0-M1.jar - org.junit.start=https://repo.maven.apache.org/maven2/org/junit/junit-start/6.1.0-M1/junit-start-6.1.0-M1.jar + org.junit.jupiter.api=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-api/{{version}}/junit-jupiter-api-{{version}}.jar + org.junit.jupiter.engine=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-engine/{{version}}/junit-jupiter-engine-{{version}}.jar + org.junit.jupiter.params=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-params/{{version}}/junit-jupiter-params-{{version}}.jar + org.junit.jupiter=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter/{{version}}/junit-jupiter-{{version}}.jar + org.junit.platform.commons=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/{{version}}/junit-platform-commons-{{version}}.jar + org.junit.platform.console=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-console/{{version}}/junit-platform-console-{{version}}.jar + org.junit.platform.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-engine/{{version}}/junit-platform-engine-{{version}}.jar + org.junit.platform.launcher=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-launcher/{{version}}/junit-platform-launcher-{{version}}.jar + org.junit.platform.reporting=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-reporting/{{version}}/junit-platform-reporting-{{version}}.jar + org.junit.platform.suite.api=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-api/{{version}}/junit-platform-suite-api-{{version}}.jar + org.junit.platform.suite.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-engine/{{version}}/junit-platform-suite-engine-{{version}}.jar + org.junit.platform.suite=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite/{{version}}/junit-platform-suite-{{version}}.jar + org.junit.start=https://repo.maven.apache.org/maven2/org/junit/junit-start/{{version}}/junit-start-{{version}}.jar org.opentest4j.reporting.tooling.spi=https://repo.maven.apache.org/maven2/org/opentest4j/reporting/open-test-reporting-tooling-spi/0.2.5/open-test-reporting-tooling-spi-0.2.5.jar org.opentest4j=https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar - """; + """.replace("{{version}}", version); void main() throws Exception { // Ensure being launched inside expected working directory diff --git a/src/Updater.java b/src/Updater.java index 5c5caf0f..9a95dbc7 100644 --- a/src/Updater.java +++ b/src/Updater.java @@ -17,7 +17,7 @@ import java.util.regex.Pattern; /** - * Updates the versions of JUnit Platform artifacts in all example projects. + * Updates the versions of JUnit Framework artifacts in all example projects. */ @SuppressWarnings({"WeakerAccess", "SameParameterValue"}) class Updater { @@ -35,6 +35,7 @@ public Updater(String newVersion) { } void update() throws IOException { + /* var gradleBomReplacement = Pattern.compile("org.junit:junit-bom:" + VERSION_REGEX); var mavenBomReplacement = Pattern.compile( """ @@ -72,6 +73,10 @@ void update() throws IOException { update(Path.of("junit-multiple-engines/build.gradle.kts"), List.of( Pattern.compile("junitBomVersion = \"" + VERSION_REGEX + '"') )); + */ + update(Path.of("junit-source-launcher/lib/DownloadRequiredModules.java"), List.of( + Pattern.compile("final String version = \"" + VERSION_REGEX + '\"') + )); } void update(Path path, List patterns) throws IOException { From 12778781344bfe6832b8ea2554ad2225fbdb7d80 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 19 Nov 2025 09:09:35 +0100 Subject: [PATCH 08/11] Remove multiline comment to update all projects --- src/Updater.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Updater.java b/src/Updater.java index 9a95dbc7..66b0f22f 100644 --- a/src/Updater.java +++ b/src/Updater.java @@ -35,7 +35,6 @@ public Updater(String newVersion) { } void update() throws IOException { - /* var gradleBomReplacement = Pattern.compile("org.junit:junit-bom:" + VERSION_REGEX); var mavenBomReplacement = Pattern.compile( """ @@ -73,7 +72,6 @@ void update() throws IOException { update(Path.of("junit-multiple-engines/build.gradle.kts"), List.of( Pattern.compile("junitBomVersion = \"" + VERSION_REGEX + '"') )); - */ update(Path.of("junit-source-launcher/lib/DownloadRequiredModules.java"), List.of( Pattern.compile("final String version = \"" + VERSION_REGEX + '\"') )); From 365102e651a9429635f89f792b773af685b62791 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 19 Nov 2025 09:30:59 +0100 Subject: [PATCH 09/11] Update StagingRepoInjector --- .../lib/DownloadRequiredModules.java | 33 ++++++++++--------- src/StagingRepoInjector.java | 5 ++- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/junit-source-launcher/lib/DownloadRequiredModules.java b/junit-source-launcher/lib/DownloadRequiredModules.java index af0ac8ec..4d2193a6 100644 --- a/junit-source-launcher/lib/DownloadRequiredModules.java +++ b/junit-source-launcher/lib/DownloadRequiredModules.java @@ -10,28 +10,31 @@ final Path lib = Path.of("lib"); // local directory to be used in module path final Set roots = Set.of("org.junit.start"); // single root module to lookup -final String version = "6.1.0-M1"; // of JUnit Platform +final String version = "6.1.0-M1"; // of JUnit Framework +final String repository = "https://repo.maven.apache.org/maven"; // of JUnit Framework final String lookup = //language=Properties """ org.apiguardian.api=https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar org.jspecify=https://repo.maven.apache.org/maven2/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar - org.junit.jupiter.api=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-api/{{version}}/junit-jupiter-api-{{version}}.jar - org.junit.jupiter.engine=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-engine/{{version}}/junit-jupiter-engine-{{version}}.jar - org.junit.jupiter.params=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-params/{{version}}/junit-jupiter-params-{{version}}.jar - org.junit.jupiter=https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter/{{version}}/junit-jupiter-{{version}}.jar - org.junit.platform.commons=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/{{version}}/junit-platform-commons-{{version}}.jar - org.junit.platform.console=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-console/{{version}}/junit-platform-console-{{version}}.jar - org.junit.platform.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-engine/{{version}}/junit-platform-engine-{{version}}.jar - org.junit.platform.launcher=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-launcher/{{version}}/junit-platform-launcher-{{version}}.jar - org.junit.platform.reporting=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-reporting/{{version}}/junit-platform-reporting-{{version}}.jar - org.junit.platform.suite.api=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-api/{{version}}/junit-platform-suite-api-{{version}}.jar - org.junit.platform.suite.engine=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite-engine/{{version}}/junit-platform-suite-engine-{{version}}.jar - org.junit.platform.suite=https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-suite/{{version}}/junit-platform-suite-{{version}}.jar - org.junit.start=https://repo.maven.apache.org/maven2/org/junit/junit-start/{{version}}/junit-start-{{version}}.jar + org.junit.jupiter.api={{repository}}/org/junit/jupiter/junit-jupiter-api/{{version}}/junit-jupiter-api-{{version}}.jar + org.junit.jupiter.engine={{repository}}/org/junit/jupiter/junit-jupiter-engine/{{version}}/junit-jupiter-engine-{{version}}.jar + org.junit.jupiter.params={{repository}}/org/junit/jupiter/junit-jupiter-params/{{version}}/junit-jupiter-params-{{version}}.jar + org.junit.jupiter={{repository}}/org/junit/jupiter/junit-jupiter/{{version}}/junit-jupiter-{{version}}.jar + org.junit.platform.commons={{repository}}/org/junit/platform/junit-platform-commons/{{version}}/junit-platform-commons-{{version}}.jar + org.junit.platform.console={{repository}}/org/junit/platform/junit-platform-console/{{version}}/junit-platform-console-{{version}}.jar + org.junit.platform.engine={{repository}}/org/junit/platform/junit-platform-engine/{{version}}/junit-platform-engine-{{version}}.jar + org.junit.platform.launcher={{repository}}/org/junit/platform/junit-platform-launcher/{{version}}/junit-platform-launcher-{{version}}.jar + org.junit.platform.reporting={{repository}}/org/junit/platform/junit-platform-reporting/{{version}}/junit-platform-reporting-{{version}}.jar + org.junit.platform.suite.api={{repository}}/org/junit/platform/junit-platform-suite-api/{{version}}/junit-platform-suite-api-{{version}}.jar + org.junit.platform.suite.engine={{repository}}/org/junit/platform/junit-platform-suite-engine/{{version}}/junit-platform-suite-engine-{{version}}.jar + org.junit.platform.suite={{repository}}/org/junit/platform/junit-platform-suite/{{version}}/junit-platform-suite-{{version}}.jar + org.junit.start={{repository}}/org/junit/junit-start/{{version}}/junit-start-{{version}}.jar org.opentest4j.reporting.tooling.spi=https://repo.maven.apache.org/maven2/org/opentest4j/reporting/open-test-reporting-tooling-spi/0.2.5/open-test-reporting-tooling-spi-0.2.5.jar org.opentest4j=https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar - """.replace("{{version}}", version); + """ + .replace("{{repository}}", repository) + .replace("{{version}}", version); void main() throws Exception { // Ensure being launched inside expected working directory diff --git a/src/StagingRepoInjector.java b/src/StagingRepoInjector.java index 3044f5f1..ea00457c 100644 --- a/src/StagingRepoInjector.java +++ b/src/StagingRepoInjector.java @@ -28,7 +28,7 @@ private StagingRepoInjector(String stagingRepoUrl) { } private void inject() throws Exception { - +/* var gradleGroovyDslSnippet = """ maven { @@ -82,6 +82,9 @@ private void inject() throws Exception { appendAfter("junit-multiple-engines/build.gradle.kts", "mavenCentral()", gradleKotlinDslSnippet); +*/ + replace("junit-source-launcher/lib/DownloadRequiredModules.java", "String repository = \"https://repo.maven.apache.org/maven\"", + "String repository = \"%s\"".formatted(stagingRepoUrl)); } void appendAfter(String path, String token, String addedContent) throws IOException { From 1313ba0e0cb4819d537d631c2a001d90a4c22c60 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 19 Nov 2025 09:46:26 +0100 Subject: [PATCH 10/11] Fix Maven Central URL --- junit-source-launcher/lib/DownloadRequiredModules.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-source-launcher/lib/DownloadRequiredModules.java b/junit-source-launcher/lib/DownloadRequiredModules.java index 4d2193a6..e97f58d3 100644 --- a/junit-source-launcher/lib/DownloadRequiredModules.java +++ b/junit-source-launcher/lib/DownloadRequiredModules.java @@ -11,7 +11,7 @@ final Path lib = Path.of("lib"); // local directory to be used in module path final Set roots = Set.of("org.junit.start"); // single root module to lookup final String version = "6.1.0-M1"; // of JUnit Framework -final String repository = "https://repo.maven.apache.org/maven"; // of JUnit Framework +final String repository = "https://repo.maven.apache.org/maven2"; // of JUnit Framework final String lookup = //language=Properties """ From 926986458a8c34225739b8a1ff8fbb2c2dbaa9d9 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Wed, 19 Nov 2025 12:02:40 +0100 Subject: [PATCH 11/11] Run all injections --- src/StagingRepoInjector.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/StagingRepoInjector.java b/src/StagingRepoInjector.java index ea00457c..d65739e0 100644 --- a/src/StagingRepoInjector.java +++ b/src/StagingRepoInjector.java @@ -28,7 +28,7 @@ private StagingRepoInjector(String stagingRepoUrl) { } private void inject() throws Exception { -/* + var gradleGroovyDslSnippet = """ maven { @@ -82,7 +82,7 @@ private void inject() throws Exception { appendAfter("junit-multiple-engines/build.gradle.kts", "mavenCentral()", gradleKotlinDslSnippet); -*/ + replace("junit-source-launcher/lib/DownloadRequiredModules.java", "String repository = \"https://repo.maven.apache.org/maven\"", "String repository = \"%s\"".formatted(stagingRepoUrl)); }