From 0e3fdda058b27ac09a5725658fe45a242aec3331 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Mon, 21 Oct 2024 15:47:18 +0200 Subject: [PATCH 01/23] Back to snapshots for further development --- gradle.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 2c411150793d..b56888f7e0ae 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,13 +1,13 @@ group = org.junit -version = 5.11.3 +version = 5.11.4-SNAPSHOT jupiterGroup = org.junit.jupiter platformGroup = org.junit.platform -platformVersion = 1.11.3 +platformVersion = 1.11.4-SNAPSHOT vintageGroup = org.junit.vintage -vintageVersion = 5.11.3 +vintageVersion = 5.11.4-SNAPSHOT # We need more metaspace due to apparent memory leak in Asciidoctor/JRuby # The exports are needed due to https://github.com/diffplug/spotless/issues/834 From aa7e1c2643029fe8ee1c4c97ff50a2ac3b85353e Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 6 Nov 2024 08:10:03 +0100 Subject: [PATCH 02/23] Inject GRADLE_ENCRYPTION_KEY for configuration cache see https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#saving-configuration-cache-data --- .github/actions/run-gradle/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/run-gradle/action.yml b/.github/actions/run-gradle/action.yml index e825551ce6f5..2c376c70220d 100644 --- a/.github/actions/run-gradle/action.yml +++ b/.github/actions/run-gradle/action.yml @@ -14,7 +14,9 @@ runs: distribution: temurin java-version: 21 check-latest: true - - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 # v4 + - uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4 + with: + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - shell: bash env: JAVA_HOME: ${{ steps.setup-gradle-jdk.outputs.path }} From 408c5f6ac9d48997838c212e4420cca31b0b240c Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 6 Nov 2024 08:57:44 +0100 Subject: [PATCH 03/23] Pass encryption key as input to composite actions Because composite actions can't access secrets directly (cherry picked from commit 6ba1f2bcd6c600c75f814e2acd1f97c0a168f071) --- .github/actions/main-build/action.yml | 4 ++++ .github/actions/run-gradle/action.yml | 5 ++++- .github/workflows/codeql-analysis.yml | 1 + .github/workflows/cross-version.yml | 2 ++ .github/workflows/main.yml | 10 +++++++++- .github/workflows/reproducible-build.yml | 1 + 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/actions/main-build/action.yml b/.github/actions/main-build/action.yml index 4c6c611412d7..b9d087000352 100644 --- a/.github/actions/main-build/action.yml +++ b/.github/actions/main-build/action.yml @@ -5,6 +5,9 @@ inputs: required: true description: Gradle arguments default: :platform-tooling-support-tests:test build --configuration-cache + encryptionKey: + required: true + description: Gradle cache encryption key runs: using: "composite" steps: @@ -12,3 +15,4 @@ runs: - uses: ./.github/actions/run-gradle with: arguments: ${{ inputs.arguments }} + encryptionKey: ${{ inputs.encryptionKey }} diff --git a/.github/actions/run-gradle/action.yml b/.github/actions/run-gradle/action.yml index 2c376c70220d..5b4ab3d092ec 100644 --- a/.github/actions/run-gradle/action.yml +++ b/.github/actions/run-gradle/action.yml @@ -5,6 +5,9 @@ inputs: required: true description: Gradle arguments default: build + encryptionKey: + required: true + description: Gradle cache encryption key runs: using: "composite" steps: @@ -16,7 +19,7 @@ runs: check-latest: true - uses: gradle/actions/setup-gradle@d156388eb19639ec20ade50009f3d199ce1e2808 # v4 with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + cache-encryption-key: ${{ inputs.encryptionKey }} - shell: bash env: JAVA_HOME: ${{ steps.setup-gradle-jdk.outputs.path }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 133179e59c25..4507189142e1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,6 +39,7 @@ jobs: - name: Build uses: ./.github/actions/run-gradle with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | --no-build-cache \ -Dscan.tag.CodeQL \ diff --git a/.github/workflows/cross-version.yml b/.github/workflows/cross-version.yml index 9df3eeea19d7..53cd670e58b7 100644 --- a/.github/workflows/cross-version.yml +++ b/.github/workflows/cross-version.yml @@ -58,6 +58,7 @@ jobs: - name: Build uses: ./.github/actions/run-gradle with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | -PjavaToolchain.version=${{ matrix.jdk.version }} \ -Dscan.tag.JDK_${{ matrix.jdk.version }} \ @@ -91,6 +92,7 @@ jobs: - name: Build uses: ./.github/actions/run-gradle with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | -PjavaToolchain.version=${{ matrix.jdk }} \ -PjavaToolchain.implementation=j9 \ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d9297eb720a2..26fc01510b32 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,6 +30,7 @@ jobs: - name: Build uses: ./.github/actions/main-build with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | -Ptesting.enableJaCoCo \ :platform-tooling-support-tests:test \ @@ -50,6 +51,8 @@ jobs: fetch-depth: 1 - name: Build uses: ./.github/actions/main-build + with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} macOS: runs-on: macos-latest @@ -60,10 +63,12 @@ jobs: fetch-depth: 1 - name: Build uses: ./.github/actions/main-build + with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} publish_artifacts: name: Publish Snapshot Artifacts - needs: linux + needs: Linux runs-on: ubuntu-latest permissions: attestations: write # required for build provenance attestation @@ -80,6 +85,7 @@ jobs: ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }} ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | publish -x check \ prepareGitHubAttestation @@ -107,6 +113,7 @@ jobs: - name: Build Documentation uses: ./.github/actions/run-gradle with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | prepareDocsForUploadToGhPages \ -Dscan.tag.Documentation @@ -114,6 +121,7 @@ jobs: if: github.event_name == 'push' && github.repository == 'junit-team/junit5' && github.ref == 'refs/heads/main' uses: ./.github/actions/run-gradle with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | gitPublishPush \ -Dscan.tag.Documentation diff --git a/.github/workflows/reproducible-build.yml b/.github/workflows/reproducible-build.yml index 39eb9160dbdc..37d6eee1b526 100644 --- a/.github/workflows/reproducible-build.yml +++ b/.github/workflows/reproducible-build.yml @@ -24,6 +24,7 @@ jobs: - name: Restore Gradle cache and display toolchains uses: ./.github/actions/run-gradle with: + encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }} arguments: | --quiet \ --configuration-cache From f275a8fe8fc9877f2157f7e13d172db1f9f94094 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 6 Nov 2024 09:56:46 +0100 Subject: [PATCH 04/23] Delete obsolete issue template (cherry picked from commit 90b4b514d5bb737c465953855798daad9643e541) --- .github/ISSUE_TEMPLATE.md | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index f1ce8b0c8472..000000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,23 +0,0 @@ -## Overview - -_Replace the following bullet points with your issue description, -after answering yourself: **"What kind of issue is this?"**_ - -- ( ) **Question.** This issue tracker is not the place for questions. -If you want to ask how to do something, or to understand why -something isn't working the way you expect it to, please first use Stack -Overflow or Gitter. -https://stackoverflow.com/questions/tagged/junit5 - -- ( ) **Bug report.** Please provide us the version of JUnit 5 you are -using and, if possible, a failing unit test with your bug report. Don't -forget to describe the rationale for this issue (e.g. expected vs. -actual behavior). - -- ( ) **Feature request.** Start by telling us what problem you’re trying -to solve. Often a solution already exists! Please, don’t send pull requests -to implement new features without first getting our support. - -## Deliverables - -- [ ] ... From ae181c8c66d8468fbde33e90b18673a2adfa9145 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Tue, 26 Nov 2024 08:55:08 +0100 Subject: [PATCH 05/23] Enable auto-flushing of output to fix `testfeed` details mode (#4153) Prior to this commit, `ConsoleLauncher` created `PrintWriters` that wouldn't flush automatically when `println` is called which stopped the `testfeed` details mode from being useful. Moreover, it didn't initialized them with the right `Charsets`. Now, we rely on Picocli's initialization again which does both correctly. (cherry picked from commit b8b5dc462c6b56527e69576001b41f1ef73a8786) --- .../junitbuild/exec/RunConsoleLauncher.kt | 18 ++++++++-- .../platform/console/ConsoleLauncher.java | 27 +++------------ .../console/options/CommandFacade.java | 18 ++++++++-- .../platform/console/options/MainCommand.java | 26 ++++++-------- .../console/options/OutputStreamConfig.java | 34 +++++++++++++++++++ .../console/ConsoleLauncherTests.java | 19 ++++------- .../console/ConsoleLauncherWrapper.java | 11 +++--- 7 files changed, 93 insertions(+), 60 deletions(-) create mode 100644 junit-platform-console/src/main/java/org/junit/platform/console/options/OutputStreamConfig.java diff --git a/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt b/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt index a92f31195ad2..896e76090c6f 100644 --- a/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt +++ b/gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt @@ -6,7 +6,13 @@ import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property -import org.gradle.api.tasks.* +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.options.Option import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.jvm.toolchain.JavaToolchainService @@ -16,7 +22,6 @@ import org.gradle.process.CommandLineArgumentProvider import org.gradle.process.ExecOperations import trackOperationSystemAsInput import java.io.ByteArrayOutputStream -import java.util.* import javax.inject.Inject @CacheableTask @@ -97,4 +102,13 @@ abstract class RunConsoleLauncher @Inject constructor(private val execOperations debugging.set(enabled) } + @Suppress("unused") + @Option( + option = "show-output", + description = "Show output" + ) + fun setShowOutput(showOutput: Boolean) { + hideOutput.set(!showOutput) + } + } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java b/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java index 6ffd9ebefa5d..54345f061e2e 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java @@ -30,36 +30,17 @@ public class ConsoleLauncher { public static void main(String... args) { - PrintWriter out = new PrintWriter(System.out); - PrintWriter err = new PrintWriter(System.err); - CommandResult result = run(out, err, args); + CommandResult result = newCommandFacade().run(args); System.exit(result.getExitCode()); } @API(status = INTERNAL, since = "1.0") public static CommandResult run(PrintWriter out, PrintWriter err, String... args) { - ConsoleLauncher consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, out, err); - return consoleLauncher.run(args); + return newCommandFacade().run(args, out, err); } - private final ConsoleTestExecutor.Factory consoleTestExecutorFactory; - private final PrintWriter out; - private final PrintWriter err; - - ConsoleLauncher(ConsoleTestExecutor.Factory consoleTestExecutorFactory, PrintWriter out, PrintWriter err) { - this.consoleTestExecutorFactory = consoleTestExecutorFactory; - this.out = out; - this.err = err; - } - - CommandResult run(String... args) { - try { - return new CommandFacade(consoleTestExecutorFactory).run(out, err, args); - } - finally { - out.flush(); - err.flush(); - } + private static CommandFacade newCommandFacade() { + return new CommandFacade(ConsoleTestExecutor::new); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java index a591a59abb70..b1690e279c96 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/CommandFacade.java @@ -33,10 +33,24 @@ public CommandFacade(ConsoleTestExecutor.Factory consoleTestExecutorFactory) { this.consoleTestExecutorFactory = consoleTestExecutorFactory; } - public CommandResult run(PrintWriter out, PrintWriter err, String[] args) { + public CommandResult run(String[] args) { + return run(args, Optional.empty()); + } + + public CommandResult run(String[] args, PrintWriter out, PrintWriter err) { + try { + return run(args, Optional.of(new OutputStreamConfig(out, err))); + } + finally { + out.flush(); + err.flush(); + } + } + + private CommandResult run(String[] args, Optional outputStreamConfig) { Optional version = ManifestVersionProvider.getImplementationVersion(); System.setProperty("junit.docs.version", version.map(it -> it.endsWith("-SNAPSHOT") ? "snapshot" : it).orElse("current")); - return new MainCommand(consoleTestExecutorFactory).run(out, err, args); + return new MainCommand(consoleTestExecutorFactory).run(args, outputStreamConfig); } } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java index d4a86e807785..941a4976ea3e 100644 --- a/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/MainCommand.java @@ -103,10 +103,11 @@ private Object runCommand(String subcommand, Optional triggeringOption) List args = new ArrayList<>(commandLine.getParseResult().expandedArgs()); triggeringOption.ifPresent(args::remove); - CommandResult result = runCommand(commandLine.getOut(), // - commandLine.getErr(), // + CommandResult result = runCommand( // + new CommandLine(command), // args.toArray(new String[0]), // - command); + Optional.of(new OutputStreamConfig(commandLine)) // + ); this.commandResult = result; printDeprecationWarning(subcommand, triggeringOption, commandLine); @@ -130,24 +131,19 @@ private static void printDeprecationWarning(String subcommand, Optional err.flush(); } - CommandResult run(PrintWriter out, PrintWriter err, String[] args) { + CommandResult run(String[] args, Optional outputStreamConfig) { CommandLine commandLine = new CommandLine(this) // .addSubcommand(new DiscoverTestsCommand(consoleTestExecutorFactory)) // .addSubcommand(new ExecuteTestsCommand(consoleTestExecutorFactory)) // .addSubcommand(new ListTestEnginesCommand()); - return runCommand(out, err, args, commandLine); + return runCommand(commandLine, args, outputStreamConfig); } - private static CommandResult runCommand(PrintWriter out, PrintWriter err, String[] args, Object command) { - return runCommand(out, err, args, new CommandLine(command)); - } - - private static CommandResult runCommand(PrintWriter out, PrintWriter err, String[] args, - CommandLine commandLine) { - int exitCode = BaseCommand.initialize(commandLine) // - .setOut(out) // - .setErr(err) // - .execute(args); + private static CommandResult runCommand(CommandLine commandLine, String[] args, + Optional outputStreamConfig) { + BaseCommand.initialize(commandLine); + outputStreamConfig.ifPresent(it -> it.applyTo(commandLine)); + int exitCode = commandLine.execute(args); return CommandResult.create(exitCode, getLikelyExecutedCommand(commandLine).getExecutionResult()); } diff --git a/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputStreamConfig.java b/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputStreamConfig.java new file mode 100644 index 000000000000..dd72ed9e9f0b --- /dev/null +++ b/junit-platform-console/src/main/java/org/junit/platform/console/options/OutputStreamConfig.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015-2024 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.console.options; + +import java.io.PrintWriter; + +import picocli.CommandLine; + +class OutputStreamConfig { + + private final PrintWriter out; + private final PrintWriter err; + + OutputStreamConfig(CommandLine commandLine) { + this(commandLine.getOut(), commandLine.getErr()); + } + + OutputStreamConfig(PrintWriter out, PrintWriter err) { + this.out = out; + this.err = err; + } + + void applyTo(CommandLine commandLine) { + commandLine.setOut(out).setErr(err); + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java index ef30a6d3db59..f4fb02f4a935 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java @@ -22,7 +22,6 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.platform.console.tasks.ConsoleTestExecutor; /** * @since 1.0 @@ -36,8 +35,7 @@ class ConsoleLauncherTests { @EmptySource @MethodSource("commandsWithEmptyOptionExitCodes") void displayHelp(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--help").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--help").getExitCode(); assertEquals(0, exitCode); assertThat(output()).contains("--help"); @@ -47,8 +45,7 @@ void displayHelp(String command) { @EmptySource @MethodSource("commandsWithEmptyOptionExitCodes") void displayVersion(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--version").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--version").getExitCode(); assertEquals(0, exitCode); assertThat(output()).contains("JUnit Platform Console Launcher"); @@ -57,8 +54,7 @@ void displayVersion(String command) { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void displayBanner(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - consoleLauncher.run(command); + ConsoleLauncher.run(printSink, printSink, command); assertThat(output()).contains("Thanks for using JUnit!"); } @@ -66,8 +62,7 @@ void displayBanner(String command) { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void disableBanner(String command, int expectedExitCode) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--disable-banner").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--disable-banner").getExitCode(); assertEquals(expectedExitCode, exitCode); assertThat(output()).doesNotContain("Thanks for using JUnit!"); @@ -76,8 +71,7 @@ void disableBanner(String command, int expectedExitCode) { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void executeWithUnknownCommandLineOption(String command) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var exitCode = consoleLauncher.run(command, "--all").getExitCode(); + var exitCode = ConsoleLauncher.run(printSink, printSink, command, "--all").getExitCode(); assertEquals(-1, exitCode); assertThat(output()).contains("Unknown option: '--all'").contains("Usage:"); @@ -90,8 +84,7 @@ private String output() { @ParameterizedTest(name = "{0}") @MethodSource("commandsWithEmptyOptionExitCodes") void executeWithoutCommandLineOptions(String command, int expectedExitCode) { - var consoleLauncher = new ConsoleLauncher(ConsoleTestExecutor::new, printSink, printSink); - var actualExitCode = consoleLauncher.run(command).getExitCode(); + var actualExitCode = ConsoleLauncher.run(printSink, printSink, command).getExitCode(); assertEquals(expectedExitCode, actualExitCode); } diff --git a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java index 7ae6ba8fd921..0d2c240d8ea3 100644 --- a/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java +++ b/platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java @@ -17,6 +17,7 @@ import java.io.StringWriter; import java.util.Optional; +import org.junit.platform.console.options.CommandFacade; import org.junit.platform.console.tasks.ConsoleTestExecutor; /** @@ -26,16 +27,14 @@ class ConsoleLauncherWrapper { private final StringWriter out = new StringWriter(); private final StringWriter err = new StringWriter(); - private final ConsoleLauncher consoleLauncher; + private final ConsoleTestExecutor.Factory consoleTestExecutorFactory; ConsoleLauncherWrapper() { this(ConsoleTestExecutor::new); } private ConsoleLauncherWrapper(ConsoleTestExecutor.Factory consoleTestExecutorFactory) { - var outWriter = new PrintWriter(out, false); - var errWriter = new PrintWriter(err, false); - this.consoleLauncher = new ConsoleLauncher(consoleTestExecutorFactory, outWriter, errWriter); + this.consoleTestExecutorFactory = consoleTestExecutorFactory; } public ConsoleLauncherWrapperResult execute(String... args) { @@ -47,7 +46,9 @@ public ConsoleLauncherWrapperResult execute(int expectedExitCode, String... args } public ConsoleLauncherWrapperResult execute(Optional expectedCode, String... args) { - var result = consoleLauncher.run(args); + var outWriter = new PrintWriter(out, false); + var errWriter = new PrintWriter(err, false); + var result = new CommandFacade(consoleTestExecutorFactory).run(args, outWriter, errWriter); var code = result.getExitCode(); var outText = out.toString(); var errText = err.toString(); From 0605e6f5cbefc42ef201f641c40be3e9125e593b Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Fri, 6 Dec 2024 20:29:21 +0100 Subject: [PATCH 06/23] Create initial 5.11.4 release notes from template (cherry picked from commit 086ac2f8317d545ba4d758e88b55c66083e1e0ea) --- .../docs/asciidoc/release-notes/index.adoc | 2 + .../release-notes/release-notes-5.11.4.adoc | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc diff --git a/documentation/src/docs/asciidoc/release-notes/index.adoc b/documentation/src/docs/asciidoc/release-notes/index.adoc index caa8abe1b4d8..814ac7aab543 100644 --- a/documentation/src/docs/asciidoc/release-notes/index.adoc +++ b/documentation/src/docs/asciidoc/release-notes/index.adoc @@ -17,6 +17,8 @@ authors as well as build tool and IDE vendors. include::{includedir}/link-attributes.adoc[] +include::{basedir}/release-notes-5.11.4.adoc[] + include::{basedir}/release-notes-5.11.3.adoc[] include::{basedir}/release-notes-5.11.2.adoc[] diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc new file mode 100644 index 000000000000..ee08a53eed41 --- /dev/null +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc @@ -0,0 +1,67 @@ +[[release-notes-5.11.4]] +== 5.11.4 + +*Date of Release:* ❓ + +*Scope:* ❓ + +For a complete list of all _closed_ issues and pull requests for this release, consult the +link:{junit5-repo}+/milestone/86?closed=1+[5.11.4] milestone page in the +JUnit repository on GitHub. + + +[[release-notes-5.11.4-junit-platform]] +=== JUnit Platform + +[[release-notes-5.11.4-junit-platform-bug-fixes]] +==== Bug Fixes + +* ❓ + +[[release-notes-5.11.4-junit-platform-deprecations-and-breaking-changes]] +==== Deprecations and Breaking Changes + +* ❓ + +[[release-notes-5.11.4-junit-platform-new-features-and-improvements]] +==== New Features and Improvements + +* ❓ + + +[[release-notes-5.11.4-junit-jupiter]] +=== JUnit Jupiter + +[[release-notes-5.11.4-junit-jupiter-bug-fixes]] +==== Bug Fixes + +* ❓ + +[[release-notes-5.11.4-junit-jupiter-deprecations-and-breaking-changes]] +==== Deprecations and Breaking Changes + +* ❓ + +[[release-notes-5.11.4-junit-jupiter-new-features-and-improvements]] +==== New Features and Improvements + +* ❓ + + +[[release-notes-5.11.4-junit-vintage]] +=== JUnit Vintage + +[[release-notes-5.11.4-junit-vintage-bug-fixes]] +==== Bug Fixes + +* ❓ + +[[release-notes-5.11.4-junit-vintage-deprecations-and-breaking-changes]] +==== Deprecations and Breaking Changes + +* ❓ + +[[release-notes-5.11.4-junit-vintage-new-features-and-improvements]] +==== New Features and Improvements + +* ❓ From 8aa220fab757e78cda6d04b5f4d596b8ebf8f962 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sat, 7 Dec 2024 21:18:45 +0100 Subject: [PATCH 07/23] Fix IntelliJ package order config to be consistent with Spotless (cherry picked from commit 803762c2b8b4a97c889cb746c06223e873438892) --- .idea/codeStyles/Project.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index e6094d409c45..9f10a217b5b9 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -9,10 +9,10 @@ - - + + From 3c868df03fef7fb2e1d574a7517e8d767b89152b Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sat, 7 Dec 2024 21:20:07 +0100 Subject: [PATCH 08/23] Add test task using Woodstox for XML serialization (cherry picked from commit 91fd95e43ed2712eb6d48efab10b9f1df821d458) --- gradle/libs.versions.toml | 1 + platform-tests/platform-tests.gradle.kts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a54336afebdc..7082f371c2fa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,6 +61,7 @@ univocity-parsers = { module = "com.univocity:univocity-parsers", version = "2.9 xmlunit-assertj = { module = "org.xmlunit:xmlunit-assertj3", version.ref = "xmlunit" } xmlunit-placeholders = { module = "org.xmlunit:xmlunit-placeholders", version.ref = "xmlunit" } testingAnnotations = { module = "com.gradle:develocity-testing-annotations", version = "2.0.1" } +woodstox = { module = "com.fasterxml.woodstox:woodstox-core", version = "7.0.0" } # Only declared here so Dependabot knows when to update the referenced versions asciidoctorj-pdf = { module = "org.asciidoctor:asciidoctorj-pdf", version.ref = "asciidoctorj-pdf" } diff --git a/platform-tests/platform-tests.gradle.kts b/platform-tests/platform-tests.gradle.kts index 183203351e94..d819495750d9 100644 --- a/platform-tests/platform-tests.gradle.kts +++ b/platform-tests/platform-tests.gradle.kts @@ -9,6 +9,12 @@ plugins { id("junitbuild.jmh-conventions") } +val woodstox = configurations.dependencyScope("woodstox") +val woodstoxRuntimeClasspath = configurations.resolvable("woodstoxRuntimeClasspath") { + extendsFrom(configurations.testRuntimeClasspath.get()) + extendsFrom(woodstox.get()) +} + dependencies { // --- Things we are testing -------------------------------------------------- testImplementation(projects.junitPlatformCommons) @@ -42,6 +48,7 @@ dependencies { testRuntimeOnly(libs.groovy4) { because("`ReflectionUtilsTests.findNestedClassesWithInvalidNestedClassFile` needs it") } + woodstox(libs.woodstox) // --- https://openjdk.java.net/projects/code-tools/jmh/ ----------------------- jmh(projects.junitJupiterApi) @@ -79,6 +86,16 @@ tasks { includeTags("junit4") } } + val testWoodstox by registering(Test::class) { + val test by testing.suites.existing(JvmTestSuite::class) + testClassesDirs = files(test.map { it.sources.output.classesDirs }) + classpath = files(sourceSets.main.map { it.output }) + files(test.map { it.sources.output }) + woodstoxRuntimeClasspath.get() + group = JavaBasePlugin.VERIFICATION_GROUP + setIncludes(listOf("**/org/junit/platform/reporting/**")) + } + check { + dependsOn(testWoodstox) + } } eclipse { From e56abce172b54b28c3a1cde4ada3fc9cd4518a52 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sat, 7 Dec 2024 21:22:33 +0100 Subject: [PATCH 09/23] Delete duplicate test relying on implementation details (cherry picked from commit 361eb6f15f674230d7b43cc2e734af022b3de04e) --- .../legacy/xml/XmlReportWriterTests.java | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java index b66b09e50e3c..0af951d3ba5a 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java @@ -227,28 +227,7 @@ void escapesInvalidCharactersInSystemPropertiesAndExceptionMessages() throws Exc .contains("AssertionError: expected: but was: "); } - @Test - void doesNotReopenCDataWithinCDataContent() throws Exception { - var uniqueId = engineDescriptor.getUniqueId().append("test", "test"); - engineDescriptor.addChild(new TestDescriptorStub(uniqueId, "test")); - var testPlan = TestPlan.from(Set.of(engineDescriptor), configParams); - - var reportData = new XmlReportData(testPlan, Clock.systemDefaultZone()); - var assertionError = new AssertionError(""); - reportData.markFinished(testPlan.getTestIdentifier(uniqueId), failed(assertionError)); - Writer assertingWriter = new StringWriter() { - - @SuppressWarnings("NullableProblems") - @Override - public void write(char[] buffer, int off, int len) { - assertThat(new String(buffer, off, len)).doesNotContain("]]> Date: Sat, 7 Dec 2024 21:27:25 +0100 Subject: [PATCH 10/23] Use Unicode replacement character for illegal characters Rather than double-escaped character references. (cherry picked from commit 7cacd27e30b5c7e5d58bf11c82957abf925ecb41) --- .../reporting/legacy/xml/XmlReportWriter.java | 14 +++++++----- .../legacy/xml/XmlReportWriterTests.java | 22 ++++++++++--------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java index e81d3abbd10c..6fa11f044fdb 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java @@ -68,6 +68,8 @@ */ class XmlReportWriter { + static final char ILLEGAL_CHARACTER_REPLACEMENT = '\uFFFD'; + // Using zero-width assertions in the split pattern simplifies the splitting process: All split parts // (including the first and last one) can be used directly, without having to re-add separator characters. private static final Pattern CDATA_SPLIT_PATTERN = Pattern.compile("(?<=]])(?=>)"); @@ -328,16 +330,16 @@ private void writeOutputElement(String elementName, String content, XMLStreamWri } private void writeAttributeSafely(XMLStreamWriter writer, String name, String value) throws XMLStreamException { - writer.writeAttribute(name, escapeIllegalChars(value)); + writer.writeAttribute(name, replaceIllegalCharacters(value)); } private void writeCDataSafely(XMLStreamWriter writer, String data) throws XMLStreamException { - for (String safeDataPart : CDATA_SPLIT_PATTERN.split(escapeIllegalChars(data))) { + for (String safeDataPart : CDATA_SPLIT_PATTERN.split(replaceIllegalCharacters(data))) { writer.writeCData(safeDataPart); } } - static String escapeIllegalChars(String text) { + static String replaceIllegalCharacters(String text) { if (text.codePoints().allMatch(XmlReportWriter::isAllowedXmlCharacter)) { return text; } @@ -346,14 +348,14 @@ static String escapeIllegalChars(String text) { if (isAllowedXmlCharacter(codePoint)) { result.appendCodePoint(codePoint); } - else { // use a Character Reference (cf. https://www.w3.org/TR/xml/#NT-CharRef) - result.append("&#").append(codePoint).append(';'); + else { + result.append(ILLEGAL_CHARACTER_REPLACEMENT); } }); return result.toString(); } - private static boolean isAllowedXmlCharacter(int codePoint) { + static boolean isAllowedXmlCharacter(int codePoint) { // source: https://www.w3.org/TR/xml/#charsets return codePoint == 0x9 // || codePoint == 0xA // diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java index 0af951d3ba5a..c56ff0086417 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java @@ -20,6 +20,7 @@ import static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY; import static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY; import static org.junit.platform.reporting.legacy.xml.XmlReportAssertions.assertValidAccordingToJenkinsSchema; +import static org.junit.platform.reporting.legacy.xml.XmlReportWriter.ILLEGAL_CHARACTER_REPLACEMENT; import static org.mockito.Mockito.mock; import java.io.StringReader; @@ -219,29 +220,30 @@ void escapesInvalidCharactersInSystemPropertiesAndExceptionMessages() throws Exc assertValidAccordingToJenkinsSchema(testsuite.document()); assertThat(testsuite.find("property").matchAttr("name", "foo\\.bar").attr("value")) // - .isEqualTo(""); + .isEqualTo(String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)); var failure = testsuite.find("failure"); assertThat(failure.attr("message")) // - .isEqualTo("expected: but was: "); + .isEqualTo("expected: but was: "); assertThat(failure.text()) // - .contains("AssertionError: expected: but was: "); + .contains("AssertionError: expected: but was: "); } - @ParameterizedTest(name = "{index}") + @ParameterizedTest(name = "[{index}]") @MethodSource("stringPairs") - void escapesIllegalChars(String input, String output) { - assertEquals(output, XmlReportWriter.escapeIllegalChars(input)); + void replacesIllegalCharacters(String input, String output) { + assertEquals(output, XmlReportWriter.replaceIllegalCharacters(input)); } static Stream stringPairs() { return Stream.of( // - arguments("\0", "�"), // - arguments("\1", ""), // + arguments("\0", String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)), // + arguments("\1", String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)), // arguments("\t", "\t"), // arguments("\r", "\r"), // arguments("\n", "\n"), // - arguments("\u001f", ""), // - arguments("\u0020", "\u0020"), // + arguments("\u001f", String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)), // + arguments("✅", "✅"), // + arguments(" ", " "), // arguments("foo!", "foo!"), // arguments("\uD801\uDC00", "\uD801\uDC00") // ); From 13a88b4e60019741170ab3a49eeb255b93bedcdf Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sat, 7 Dec 2024 21:30:06 +0100 Subject: [PATCH 11/23] Add test for whitespace escaping (cherry picked from commit 20092d1372452970d7d6af848d28f34804bf91f0) --- platform-tests/platform-tests.gradle.kts | 4 +-- .../legacy/xml/XmlReportWriterTests.java | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/platform-tests/platform-tests.gradle.kts b/platform-tests/platform-tests.gradle.kts index d819495750d9..a857b163b81c 100644 --- a/platform-tests/platform-tests.gradle.kts +++ b/platform-tests/platform-tests.gradle.kts @@ -11,8 +11,8 @@ plugins { val woodstox = configurations.dependencyScope("woodstox") val woodstoxRuntimeClasspath = configurations.resolvable("woodstoxRuntimeClasspath") { - extendsFrom(configurations.testRuntimeClasspath.get()) - extendsFrom(woodstox.get()) + extendsFrom(configurations.testRuntimeClasspath.get()) + extendsFrom(woodstox.get()) } dependencies { diff --git a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java index c56ff0086417..7a5b49f38c40 100644 --- a/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java +++ b/platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java @@ -10,6 +10,7 @@ package org.junit.platform.reporting.legacy.xml; +import static java.util.stream.Collectors.joining; import static org.assertj.core.api.Assertions.assertThat; import static org.joox.JOOX.$; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -29,6 +30,7 @@ import java.time.Clock; import java.util.Map; import java.util.Set; +import java.util.stream.IntStream; import java.util.stream.Stream; import org.joox.Match; @@ -234,6 +236,31 @@ void replacesIllegalCharacters(String input, String output) { assertEquals(output, XmlReportWriter.replaceIllegalCharacters(input)); } + @Test + void writesValidXmlForExceptionMessagesContainingLineBreaks() throws Exception { + var uniqueId = engineDescriptor.getUniqueId().append("test", "test"); + engineDescriptor.addChild(new TestDescriptorStub(uniqueId, "test")); + var testPlan = TestPlan.from(Set.of(engineDescriptor), configParams); + + var allWhitespaceCharacters = IntStream.range(0, 0x10000) // + .filter(Character::isWhitespace) // + .filter(XmlReportWriter::isAllowedXmlCharacter) // + .mapToObj(Character::toString) // + .collect(joining()); + + var message = "a" + allWhitespaceCharacters + " b<&>"; + var reportData = new XmlReportData(testPlan, Clock.systemDefaultZone()); + var assertionError = new AssertionError(message); + reportData.markFinished(testPlan.getTestIdentifier(uniqueId), failed(assertionError)); + + var testsuite = writeXmlReport(testPlan, reportData); + + assertValidAccordingToJenkinsSchema(testsuite.document()); + + var attributeValue = testsuite.find("failure").attr("message"); + assertThat(attributeValue).isEqualTo(message); + } + static Stream stringPairs() { return Stream.of( // arguments("\0", String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)), // From dac61cc1f96e8eae47a95e5dd58e5bbcc5b2c64a Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sun, 8 Dec 2024 13:08:11 +0100 Subject: [PATCH 12/23] Implement workaround for escaping whitespace chars in attribute values While XML attribute values may contain whitespace such as line breaks, the XML spec [1] dictates that XML processors must replace them with spaces which causes downstream tools to misrepresent the original value. [1] w3.org/TR/xml#AVNormalize Resolves #4174. (cherry picked from commit 432d556bc7a972f5a94e856f30465b7ad7d2f915) --- .../release-notes/release-notes-5.11.4.adoc | 5 +- .../reporting/legacy/xml/XmlReportWriter.java | 526 +++++++++++------- 2 files changed, 341 insertions(+), 190 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc index ee08a53eed41..1e2d0eab31cc 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc @@ -16,7 +16,10 @@ JUnit repository on GitHub. [[release-notes-5.11.4-junit-platform-bug-fixes]] ==== Bug Fixes -* ❓ +* Escape whitespace characters (such as line breaks) in XML attribute values (such as + exception messages) in the legacy XML report generated by the Console Launcher. This + change ensures the resulting XML files can be processed by downstream tools while + preserving whitespace characters. [[release-notes-5.11.4-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java index 6fa11f044fdb..b9d7ea13aff3 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java @@ -13,6 +13,7 @@ import static java.text.MessageFormat.format; import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME; import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableMap; import static java.util.Comparator.naturalOrder; import static java.util.function.Function.identity; import static java.util.stream.Collectors.counting; @@ -30,6 +31,7 @@ import static org.junit.platform.reporting.legacy.xml.XmlReportWriter.AggregatedTestResult.Type.SKIPPED; import static org.junit.platform.reporting.legacy.xml.XmlReportWriter.AggregatedTestResult.Type.SUCCESS; +import java.io.IOException; import java.io.Writer; import java.net.InetAddress; import java.net.UnknownHostException; @@ -38,6 +40,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -70,6 +73,15 @@ class XmlReportWriter { static final char ILLEGAL_CHARACTER_REPLACEMENT = '\uFFFD'; + private static final Map REPLACEMENTS_IN_ATTRIBUTE_VALUES; + static { + Map tmp = new HashMap<>(3); + tmp.put('\n', " "); + tmp.put('\r', " "); + tmp.put('\t', " "); + REPLACEMENTS_IN_ATTRIBUTE_VALUES = unmodifiableMap(tmp); + } + // Using zero-width assertions in the split pattern simplifies the splitting process: All split parts // (including the first and last one) can be used directly, without having to re-add separator characters. private static final Pattern CDATA_SPLIT_PATTERN = Pattern.compile("(?<=]])(?=>)"); @@ -103,239 +115,265 @@ private boolean shouldInclude(TestPlan testPlan, TestIdentifier testIdentifier) private void writeXmlReport(TestIdentifier testIdentifier, Map tests, Writer out) throws XMLStreamException { - XMLOutputFactory factory = XMLOutputFactory.newInstance(); - XMLStreamWriter xmlWriter = factory.createXMLStreamWriter(out); - xmlWriter.writeStartDocument("UTF-8", "1.0"); - newLine(xmlWriter); - writeTestsuite(testIdentifier, tests, xmlWriter); - xmlWriter.writeEndDocument(); - xmlWriter.flush(); - xmlWriter.close(); + new XmlReport(out).write(testIdentifier, tests); } - private void writeTestsuite(TestIdentifier testIdentifier, Map tests, - XMLStreamWriter writer) throws XMLStreamException { + class XmlReport implements AutoCloseable { - // NumberFormat is not thread-safe. Thus, we instantiate it here and pass it to - // writeTestcase instead of using a constant - NumberFormat numberFormat = NumberFormat.getInstance(Locale.US); + private final XMLStreamWriter xml; + private final ReplacingWriter out; - writer.writeStartElement("testsuite"); + XmlReport(Writer out) throws XMLStreamException { + this.out = new ReplacingWriter(out); + XMLOutputFactory factory = XMLOutputFactory.newInstance(); + this.xml = factory.createXMLStreamWriter(this.out); + } - writeSuiteAttributes(testIdentifier, tests.values(), numberFormat, writer); + void write(TestIdentifier testIdentifier, Map tests) + throws XMLStreamException { + xml.writeStartDocument("UTF-8", "1.0"); + newLine(); + writeTestsuite(testIdentifier, tests); + xml.writeEndDocument(); + } - newLine(writer); - writeSystemProperties(writer); + private void writeTestsuite(TestIdentifier testIdentifier, Map tests) + throws XMLStreamException { - for (Entry entry : tests.entrySet()) { - writeTestcase(entry.getKey(), entry.getValue(), numberFormat, writer); - } + // NumberFormat is not thread-safe. Thus, we instantiate it here and pass it to + // writeTestcase instead of using a constant + NumberFormat numberFormat = NumberFormat.getInstance(Locale.US); - writeOutputElement("system-out", formatNonStandardAttributesAsString(testIdentifier), writer); + xml.writeStartElement("testsuite"); - writer.writeEndElement(); - newLine(writer); - } + writeSuiteAttributes(testIdentifier, tests.values(), numberFormat); - private void writeSuiteAttributes(TestIdentifier testIdentifier, Collection testResults, - NumberFormat numberFormat, XMLStreamWriter writer) throws XMLStreamException { + newLine(); + writeSystemProperties(); - writeAttributeSafely(writer, "name", testIdentifier.getDisplayName()); - writeTestCounts(testResults, writer); - writeAttributeSafely(writer, "time", getTime(testIdentifier, numberFormat)); - writeAttributeSafely(writer, "hostname", getHostname().orElse("")); - writeAttributeSafely(writer, "timestamp", ISO_LOCAL_DATE_TIME.format(getCurrentDateTime())); - } + for (Entry entry : tests.entrySet()) { + writeTestcase(entry.getKey(), entry.getValue(), numberFormat); + } - private void writeTestCounts(Collection testResults, XMLStreamWriter writer) - throws XMLStreamException { - Map counts = testResults.stream().map(it -> it.type).collect(groupingBy(identity(), counting())); - long total = counts.values().stream().mapToLong(Long::longValue).sum(); - writeAttributeSafely(writer, "tests", String.valueOf(total)); - writeAttributeSafely(writer, "skipped", counts.getOrDefault(SKIPPED, 0L).toString()); - writeAttributeSafely(writer, "failures", counts.getOrDefault(FAILURE, 0L).toString()); - writeAttributeSafely(writer, "errors", counts.getOrDefault(ERROR, 0L).toString()); - } + writeOutputElement("system-out", formatNonStandardAttributesAsString(testIdentifier)); - private void writeSystemProperties(XMLStreamWriter writer) throws XMLStreamException { - writer.writeStartElement("properties"); - newLine(writer); - Properties systemProperties = System.getProperties(); - for (String propertyName : new TreeSet<>(systemProperties.stringPropertyNames())) { - writer.writeEmptyElement("property"); - writeAttributeSafely(writer, "name", propertyName); - writeAttributeSafely(writer, "value", systemProperties.getProperty(propertyName)); - newLine(writer); - } - writer.writeEndElement(); - newLine(writer); - } + xml.writeEndElement(); + newLine(); + } - private void writeTestcase(TestIdentifier testIdentifier, AggregatedTestResult testResult, - NumberFormat numberFormat, XMLStreamWriter writer) throws XMLStreamException { + private void writeSuiteAttributes(TestIdentifier testIdentifier, Collection testResults, + NumberFormat numberFormat) throws XMLStreamException { - writer.writeStartElement("testcase"); + writeAttributeSafely("name", testIdentifier.getDisplayName()); + writeTestCounts(testResults); + writeAttributeSafely("time", getTime(testIdentifier, numberFormat)); + writeAttributeSafely("hostname", getHostname().orElse("")); + writeAttributeSafely("timestamp", ISO_LOCAL_DATE_TIME.format(getCurrentDateTime())); + } - writeAttributeSafely(writer, "name", getName(testIdentifier)); - writeAttributeSafely(writer, "classname", getClassName(testIdentifier)); - writeAttributeSafely(writer, "time", getTime(testIdentifier, numberFormat)); - newLine(writer); + private void writeTestCounts(Collection testResults) throws XMLStreamException { + Map counts = testResults.stream().map(it -> it.type).collect( + groupingBy(identity(), counting())); + long total = counts.values().stream().mapToLong(Long::longValue).sum(); + writeAttributeSafely("tests", String.valueOf(total)); + writeAttributeSafely("skipped", counts.getOrDefault(SKIPPED, 0L).toString()); + writeAttributeSafely("failures", counts.getOrDefault(FAILURE, 0L).toString()); + writeAttributeSafely("errors", counts.getOrDefault(ERROR, 0L).toString()); + } - writeSkippedOrErrorOrFailureElement(testIdentifier, testResult, writer); + private void writeSystemProperties() throws XMLStreamException { + xml.writeStartElement("properties"); + newLine(); + Properties systemProperties = System.getProperties(); + for (String propertyName : new TreeSet<>(systemProperties.stringPropertyNames())) { + xml.writeEmptyElement("property"); + writeAttributeSafely("name", propertyName); + writeAttributeSafely("value", systemProperties.getProperty(propertyName)); + newLine(); + } + xml.writeEndElement(); + newLine(); + } - List systemOutElements = new ArrayList<>(); - List systemErrElements = new ArrayList<>(); - systemOutElements.add(formatNonStandardAttributesAsString(testIdentifier)); - collectReportEntries(testIdentifier, systemOutElements, systemErrElements); - writeOutputElements("system-out", systemOutElements, writer); - writeOutputElements("system-err", systemErrElements, writer); + private void writeTestcase(TestIdentifier testIdentifier, AggregatedTestResult testResult, + NumberFormat numberFormat) throws XMLStreamException { - writer.writeEndElement(); - newLine(writer); - } + xml.writeStartElement("testcase"); - private String getName(TestIdentifier testIdentifier) { - return testIdentifier.getLegacyReportingName(); - } + writeAttributeSafely("name", getName(testIdentifier)); + writeAttributeSafely("classname", getClassName(testIdentifier)); + writeAttributeSafely("time", getTime(testIdentifier, numberFormat)); + newLine(); - private String getClassName(TestIdentifier testIdentifier) { - return LegacyReportingUtils.getClassName(this.reportData.getTestPlan(), testIdentifier); - } + writeSkippedOrErrorOrFailureElement(testIdentifier, testResult); + + List systemOutElements = new ArrayList<>(); + List systemErrElements = new ArrayList<>(); + systemOutElements.add(formatNonStandardAttributesAsString(testIdentifier)); + collectReportEntries(testIdentifier, systemOutElements, systemErrElements); + writeOutputElements("system-out", systemOutElements); + writeOutputElements("system-err", systemErrElements); + + xml.writeEndElement(); + newLine(); + } - private void writeSkippedOrErrorOrFailureElement(TestIdentifier testIdentifier, AggregatedTestResult testResult, - XMLStreamWriter writer) throws XMLStreamException { + private String getName(TestIdentifier testIdentifier) { + return testIdentifier.getLegacyReportingName(); + } - if (testResult.type == SKIPPED) { - writeSkippedElement(this.reportData.getSkipReason(testIdentifier), writer); + private String getClassName(TestIdentifier testIdentifier) { + return LegacyReportingUtils.getClassName(reportData.getTestPlan(), testIdentifier); } - else { - Map>> throwablesByType = testResult.getThrowablesByType(); - for (Type type : EnumSet.of(FAILURE, ERROR)) { - for (Optional throwable : throwablesByType.getOrDefault(type, emptyList())) { - writeErrorOrFailureElement(type, throwable.orElse(null), writer); + + private void writeSkippedOrErrorOrFailureElement(TestIdentifier testIdentifier, AggregatedTestResult testResult) + throws XMLStreamException { + + if (testResult.type == SKIPPED) { + writeSkippedElement(reportData.getSkipReason(testIdentifier), xml); + } + else { + Map>> throwablesByType = testResult.getThrowablesByType(); + for (Type type : EnumSet.of(FAILURE, ERROR)) { + for (Optional throwable : throwablesByType.getOrDefault(type, emptyList())) { + writeErrorOrFailureElement(type, throwable.orElse(null), xml); + } } } } - } - private void writeSkippedElement(String reason, XMLStreamWriter writer) throws XMLStreamException { - if (isNotBlank(reason)) { - writer.writeStartElement("skipped"); - writeCDataSafely(writer, reason); - writer.writeEndElement(); - } - else { - writer.writeEmptyElement("skipped"); + private void writeSkippedElement(String reason, XMLStreamWriter writer) throws XMLStreamException { + if (isNotBlank(reason)) { + writer.writeStartElement("skipped"); + writeCDataSafely(reason); + writer.writeEndElement(); + } + else { + writer.writeEmptyElement("skipped"); + } + newLine(); } - newLine(writer); - } - private void writeErrorOrFailureElement(Type type, Throwable throwable, XMLStreamWriter writer) - throws XMLStreamException { + private void writeErrorOrFailureElement(Type type, Throwable throwable, XMLStreamWriter writer) + throws XMLStreamException { - String elementName = type == FAILURE ? "failure" : "error"; - if (throwable != null) { - writer.writeStartElement(elementName); - writeFailureAttributesAndContent(throwable, writer); - writer.writeEndElement(); - } - else { - writer.writeEmptyElement(elementName); + String elementName = type == FAILURE ? "failure" : "error"; + if (throwable != null) { + writer.writeStartElement(elementName); + writeFailureAttributesAndContent(throwable); + writer.writeEndElement(); + } + else { + writer.writeEmptyElement(elementName); + } + newLine(); } - newLine(writer); - } - private void writeFailureAttributesAndContent(Throwable throwable, XMLStreamWriter writer) - throws XMLStreamException { + private void writeFailureAttributesAndContent(Throwable throwable) throws XMLStreamException { - if (throwable.getMessage() != null) { - writeAttributeSafely(writer, "message", throwable.getMessage()); + if (throwable.getMessage() != null) { + writeAttributeSafely("message", throwable.getMessage()); + } + writeAttributeSafely("type", throwable.getClass().getName()); + writeCDataSafely(readStackTrace(throwable)); } - writeAttributeSafely(writer, "type", throwable.getClass().getName()); - writeCDataSafely(writer, readStackTrace(throwable)); - } - private void collectReportEntries(TestIdentifier testIdentifier, List systemOutElements, - List systemErrElements) { - List entries = this.reportData.getReportEntries(testIdentifier); - if (!entries.isEmpty()) { - List systemOutElementsForCapturedOutput = new ArrayList<>(); - StringBuilder formattedReportEntries = new StringBuilder(); - for (int i = 0; i < entries.size(); i++) { - ReportEntry reportEntry = entries.get(i); - Map keyValuePairs = new LinkedHashMap<>(reportEntry.getKeyValuePairs()); - removeIfPresentAndAddAsSeparateElement(keyValuePairs, STDOUT_REPORT_ENTRY_KEY, - systemOutElementsForCapturedOutput); - removeIfPresentAndAddAsSeparateElement(keyValuePairs, STDERR_REPORT_ENTRY_KEY, systemErrElements); - if (!keyValuePairs.isEmpty()) { - buildReportEntryDescription(reportEntry.getTimestamp(), keyValuePairs, i + 1, - formattedReportEntries); + private void collectReportEntries(TestIdentifier testIdentifier, List systemOutElements, + List systemErrElements) { + List entries = reportData.getReportEntries(testIdentifier); + if (!entries.isEmpty()) { + List systemOutElementsForCapturedOutput = new ArrayList<>(); + StringBuilder formattedReportEntries = new StringBuilder(); + for (int i = 0; i < entries.size(); i++) { + ReportEntry reportEntry = entries.get(i); + Map keyValuePairs = new LinkedHashMap<>(reportEntry.getKeyValuePairs()); + removeIfPresentAndAddAsSeparateElement(keyValuePairs, STDOUT_REPORT_ENTRY_KEY, + systemOutElementsForCapturedOutput); + removeIfPresentAndAddAsSeparateElement(keyValuePairs, STDERR_REPORT_ENTRY_KEY, systemErrElements); + if (!keyValuePairs.isEmpty()) { + buildReportEntryDescription(reportEntry.getTimestamp(), keyValuePairs, i + 1, + formattedReportEntries); + } } + systemOutElements.add(formattedReportEntries.toString().trim()); + systemOutElements.addAll(systemOutElementsForCapturedOutput); } - systemOutElements.add(formattedReportEntries.toString().trim()); - systemOutElements.addAll(systemOutElementsForCapturedOutput); } - } - private void removeIfPresentAndAddAsSeparateElement(Map keyValuePairs, String key, - List elements) { - String value = keyValuePairs.remove(key); - if (value != null) { - elements.add(value); + private void removeIfPresentAndAddAsSeparateElement(Map keyValuePairs, String key, + List elements) { + String value = keyValuePairs.remove(key); + if (value != null) { + elements.add(value); + } } - } - private void buildReportEntryDescription(LocalDateTime timestamp, Map keyValuePairs, - int entryNumber, StringBuilder result) { - result.append( - format("Report Entry #{0} (timestamp: {1})\n", entryNumber, ISO_LOCAL_DATE_TIME.format(timestamp))); - keyValuePairs.forEach((key, value) -> result.append(format("\t- {0}: {1}\n", key, value))); - } + private void buildReportEntryDescription(LocalDateTime timestamp, Map keyValuePairs, + int entryNumber, StringBuilder result) { + result.append( + format("Report Entry #{0} (timestamp: {1})\n", entryNumber, ISO_LOCAL_DATE_TIME.format(timestamp))); + keyValuePairs.forEach((key, value) -> result.append(format("\t- {0}: {1}\n", key, value))); + } - private String getTime(TestIdentifier testIdentifier, NumberFormat numberFormat) { - return numberFormat.format(this.reportData.getDurationInSeconds(testIdentifier)); - } + private String getTime(TestIdentifier testIdentifier, NumberFormat numberFormat) { + return numberFormat.format(reportData.getDurationInSeconds(testIdentifier)); + } - private Optional getHostname() { - try { - return Optional.ofNullable(InetAddress.getLocalHost().getHostName()); + private Optional getHostname() { + try { + return Optional.ofNullable(InetAddress.getLocalHost().getHostName()); + } + catch (UnknownHostException e) { + return Optional.empty(); + } } - catch (UnknownHostException e) { - return Optional.empty(); + + private LocalDateTime getCurrentDateTime() { + return LocalDateTime.now(reportData.getClock()).withNano(0); } - } - private LocalDateTime getCurrentDateTime() { - return LocalDateTime.now(this.reportData.getClock()).withNano(0); - } + private String formatNonStandardAttributesAsString(TestIdentifier testIdentifier) { + return "unique-id: " + testIdentifier.getUniqueId() // + + "\ndisplay-name: " + testIdentifier.getDisplayName(); + } - private String formatNonStandardAttributesAsString(TestIdentifier testIdentifier) { - return "unique-id: " + testIdentifier.getUniqueId() // - + "\ndisplay-name: " + testIdentifier.getDisplayName(); - } + private void writeOutputElements(String elementName, List elements) throws XMLStreamException { + for (String content : elements) { + writeOutputElement(elementName, content); + } + } - private void writeOutputElements(String elementName, List elements, XMLStreamWriter writer) - throws XMLStreamException { - for (String content : elements) { - writeOutputElement(elementName, content, writer); + private void writeOutputElement(String elementName, String content) throws XMLStreamException { + xml.writeStartElement(elementName); + writeCDataSafely("\n" + content + "\n"); + xml.writeEndElement(); + newLine(); } - } - private void writeOutputElement(String elementName, String content, XMLStreamWriter writer) - throws XMLStreamException { - writer.writeStartElement(elementName); - writeCDataSafely(writer, "\n" + content + "\n"); - writer.writeEndElement(); - newLine(writer); - } + private void writeAttributeSafely(String name, String value) throws XMLStreamException { + // Workaround for XMLStreamWriter implementations that don't escape + // '\n', '\r', and '\t' characters in attribute values + xml.flush(); + out.setWhitespaceReplacingEnabled(true); + xml.writeAttribute(name, replaceIllegalCharacters(value)); + xml.flush(); + out.setWhitespaceReplacingEnabled(false); + } - private void writeAttributeSafely(XMLStreamWriter writer, String name, String value) throws XMLStreamException { - writer.writeAttribute(name, replaceIllegalCharacters(value)); - } + private void writeCDataSafely(String data) throws XMLStreamException { + for (String safeDataPart : CDATA_SPLIT_PATTERN.split(replaceIllegalCharacters(data))) { + xml.writeCData(safeDataPart); + } + } - private void writeCDataSafely(XMLStreamWriter writer, String data) throws XMLStreamException { - for (String safeDataPart : CDATA_SPLIT_PATTERN.split(replaceIllegalCharacters(data))) { - writer.writeCData(safeDataPart); + private void newLine() throws XMLStreamException { + xml.writeCharacters("\n"); + } + + @Override + public void close() throws XMLStreamException { + xml.flush(); + xml.close(); } } @@ -365,15 +403,6 @@ static boolean isAllowedXmlCharacter(int codePoint) { || (codePoint >= 0x10000 && codePoint <= 0x10FFFF); } - private void newLine(XMLStreamWriter xmlWriter) throws XMLStreamException { - xmlWriter.writeCharacters("\n"); - } - - private static boolean isFailure(TestExecutionResult result) { - Optional throwable = result.getThrowable(); - return throwable.isPresent() && throwable.get() instanceof AssertionError; - } - static class AggregatedTestResult { private static final AggregatedTestResult SKIPPED_RESULT = new AggregatedTestResult(SKIPPED, emptyList()); @@ -413,6 +442,125 @@ private static Type from(TestExecutionResult executionResult) { } return SUCCESS; } + + private static boolean isFailure(TestExecutionResult result) { + Optional throwable = result.getThrowable(); + return throwable.isPresent() && throwable.get() instanceof AssertionError; + } + } + } + + private static class ReplacingWriter extends Writer { + + private final Writer delegate; + private boolean whitespaceReplacingEnabled; + + ReplacingWriter(Writer delegate) { + this.delegate = delegate; + } + + void setWhitespaceReplacingEnabled(boolean whitespaceReplacingEnabled) { + this.whitespaceReplacingEnabled = whitespaceReplacingEnabled; + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + if (!whitespaceReplacingEnabled) { + delegate.write(cbuf, off, len); + return; + } + StringBuilder stringBuilder = new StringBuilder(len * 2); + for (int i = off; i < off + len; i++) { + char c = cbuf[i]; + String replacement = REPLACEMENTS_IN_ATTRIBUTE_VALUES.get(c); + if (replacement != null) { + stringBuilder.append(replacement); + } + else { + stringBuilder.append(c); + } + } + delegate.write(stringBuilder.toString()); + } + + @Override + public void write(int c) throws IOException { + if (whitespaceReplacingEnabled) { + super.write(c); + } + else { + delegate.write(c); + } + } + + @Override + public void write(char[] cbuf) throws IOException { + if (whitespaceReplacingEnabled) { + super.write(cbuf); + } + else { + delegate.write(cbuf); + } + } + + @Override + public void write(String str) throws IOException { + if (whitespaceReplacingEnabled) { + super.write(str); + } + else { + delegate.write(str); + } + } + + @Override + public void write(String str, int off, int len) throws IOException { + if (whitespaceReplacingEnabled) { + super.write(str, off, len); + } + else { + delegate.write(str, off, len); + } + } + + @Override + public Writer append(CharSequence csq) throws IOException { + if (whitespaceReplacingEnabled) { + return super.append(csq); + } + else { + return delegate.append(csq); + } + } + + @Override + public Writer append(CharSequence csq, int start, int end) throws IOException { + if (whitespaceReplacingEnabled) { + return super.append(csq, start, end); + } + else { + return delegate.append(csq, start, end); + } + } + + @Override + public Writer append(char c) throws IOException { + if (whitespaceReplacingEnabled) { + return super.append(c); + } + else { + return delegate.append(c); + } + } + + @Override + public void flush() throws IOException { + delegate.flush(); + } + + @Override + public void close() throws IOException { + delegate.close(); } } From 70e91bf9e9be7f128fa889bcc7a4db6227f504b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Dec 2024 19:37:04 +0000 Subject: [PATCH 13/23] Update dependency com.fasterxml.woodstox:woodstox-core to v7.1.0 (cherry picked from commit 2206087185eaf26e0eb3a6c9b26e5532705bf344) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7082f371c2fa..96ecacb92c41 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,7 +61,7 @@ univocity-parsers = { module = "com.univocity:univocity-parsers", version = "2.9 xmlunit-assertj = { module = "org.xmlunit:xmlunit-assertj3", version.ref = "xmlunit" } xmlunit-placeholders = { module = "org.xmlunit:xmlunit-placeholders", version.ref = "xmlunit" } testingAnnotations = { module = "com.gradle:develocity-testing-annotations", version = "2.0.1" } -woodstox = { module = "com.fasterxml.woodstox:woodstox-core", version = "7.0.0" } +woodstox = { module = "com.fasterxml.woodstox:woodstox-core", version = "7.1.0" } # Only declared here so Dependabot knows when to update the referenced versions asciidoctorj-pdf = { module = "org.asciidoctor:asciidoctorj-pdf", version.ref = "asciidoctorj-pdf" } From 3d11279dbaae5aac0ab5f28d8283272bdbca924f Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Mon, 9 Dec 2024 11:14:32 +0100 Subject: [PATCH 14/23] Add `JAVA_25` to `JRE` enum Add `JRE.JAVA_25` constant and introduce JDK 25-ea CI build Closes #4177 (cherry picked from commit e3715cfdf9d59d82de7a94b90964d7578ebd6d8e) --- .github/workflows/cross-version.yml | 2 ++ .../src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc | 2 +- gradle/base/code-generator-model/src/main/resources/jre.yaml | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cross-version.yml b/.github/workflows/cross-version.yml index 53cd670e58b7..789f7aa9a088 100644 --- a/.github/workflows/cross-version.yml +++ b/.github/workflows/cross-version.yml @@ -29,6 +29,8 @@ jobs: - version: 24 type: ea release: leyden + - version: 25 + type: ea name: "OpenJDK ${{ matrix.jdk.version }} (${{ matrix.jdk.release || matrix.jdk.type }})" runs-on: ubuntu-latest steps: diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc index 1e2d0eab31cc..ce1fd83e14c4 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc @@ -48,7 +48,7 @@ JUnit repository on GitHub. [[release-notes-5.11.4-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements -* ❓ +* `JAVA_25` has been added to the `JRE` enum for use with JRE-based execution conditions. [[release-notes-5.11.4-junit-vintage]] diff --git a/gradle/base/code-generator-model/src/main/resources/jre.yaml b/gradle/base/code-generator-model/src/main/resources/jre.yaml index ed1bdd3d59fc..1747ffa12dd6 100644 --- a/gradle/base/code-generator-model/src/main/resources/jre.yaml +++ b/gradle/base/code-generator-model/src/main/resources/jre.yaml @@ -28,3 +28,5 @@ since: '5.11' - version: 24 since: '5.11' +- version: 25 + since: '5.11.4' From d67196188fb63fa5a35f63caf168dc42cecfaca8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 20:28:26 +0100 Subject: [PATCH 15/23] Update plugin gitPublish to v5 (cherry picked from commit bc3ea9236578a53005b772f7f18aee7d2bf01cb9) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 96ecacb92c41..8a234a8b19f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -84,7 +84,7 @@ buildParameters = { id = "org.gradlex.build-parameters", version = "1.4.4" } commonCustomUserData = { id = "com.gradle.common-custom-user-data-gradle-plugin", version = "2.0.2" } develocity = { id = "com.gradle.develocity", version = "3.17.6" } foojayResolver = { id = "org.gradle.toolchains.foojay-resolver", version = "0.8.0" } -gitPublish = { id = "org.ajoberstar.git-publish", version = "4.2.2" } +gitPublish = { id = "org.ajoberstar.git-publish", version = "5.1.0" } jmh = { id = "me.champeau.jmh", version = "0.7.2" } nexusPublish = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" } nohttp = { id = "io.spring.nohttp", version = "0.0.11" } From 500b5a06b5964a477e65719877653bae0e2496fc Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 11 Dec 2024 09:29:06 +0100 Subject: [PATCH 16/23] Inject username and password via new DSL (cherry picked from commit 378cdd5adde25ed339f284c22427b0ed13ea39dc) --- .github/workflows/main.yml | 3 ++- documentation/documentation.gradle.kts | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 26fc01510b32..b00b1e6cf74a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -126,4 +126,5 @@ jobs: gitPublishPush \ -Dscan.tag.Documentation env: - GRGIT_USER: ${{ secrets.GH_TOKEN }} + GIT_USERNAME: git + GIT_PASSWORD: ${{ secrets.GH_TOKEN }} diff --git a/documentation/documentation.gradle.kts b/documentation/documentation.gradle.kts index f5f6e1b6391a..189da31bb240 100644 --- a/documentation/documentation.gradle.kts +++ b/documentation/documentation.gradle.kts @@ -108,6 +108,9 @@ gitPublish { sign = false fetchDepth = 1 + username = providers.environmentVariable("GIT_USERNAME") + password = providers.environmentVariable("GIT_PASSWORD") + contents { from(docsDir) into("docs") From 2b485c4286531fe7f3aa70367a27cf141c669a12 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 11 Dec 2024 09:29:52 +0100 Subject: [PATCH 17/23] Set reference repo URI (cherry picked from commit 4f294732024fdd4fc70ae14b5236c7f9fa237202) --- documentation/documentation.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/documentation.gradle.kts b/documentation/documentation.gradle.kts index 189da31bb240..d092724e92d8 100644 --- a/documentation/documentation.gradle.kts +++ b/documentation/documentation.gradle.kts @@ -104,6 +104,8 @@ val apiGuardianDocVersion = if (libs.versions.apiguardian.get().contains("SNAPSH gitPublish { repoUri = "https://github.com/junit-team/junit5.git" + referenceRepoUri = rootDir.toURI().toString() + branch = "gh-pages" sign = false fetchDepth = 1 From 6376f0ab367f1ac17ce75b5410e68090b03b9d9b Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 11 Dec 2024 09:30:33 +0100 Subject: [PATCH 18/23] Configure Git username and email (cherry picked from commit dab4e51058fd9083bffe5e6352b7264236fc2a2f) --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b00b1e6cf74a..357ed0d1cf21 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -117,6 +117,11 @@ jobs: arguments: | prepareDocsForUploadToGhPages \ -Dscan.tag.Documentation + - name: Configure Git + shell: bash + run: | + git config --global user.name "JUnit Team" + git config --global user.email "team@junit.org" - name: Upload Documentation if: github.event_name == 'push' && github.repository == 'junit-team/junit5' && github.ref == 'refs/heads/main' uses: ./.github/actions/run-gradle From b20c4e2eaed8a97536d48f7bb084a4bd828a56a9 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 11 Dec 2024 16:46:47 +0100 Subject: [PATCH 19/23] Ensure the XMLStreamWriter is closed after use Co-authored-by: Christian Stein (cherry picked from commit 6a4cb0643b7d49dccbebaba8a7935331e4023022) --- .../platform/reporting/legacy/xml/XmlReportWriter.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java index b9d7ea13aff3..4ea8c2f8f6ac 100644 --- a/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java +++ b/junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java @@ -115,10 +115,12 @@ private boolean shouldInclude(TestPlan testPlan, TestIdentifier testIdentifier) private void writeXmlReport(TestIdentifier testIdentifier, Map tests, Writer out) throws XMLStreamException { - new XmlReport(out).write(testIdentifier, tests); + try (XmlReport report = new XmlReport(out)) { + report.write(testIdentifier, tests); + } } - class XmlReport implements AutoCloseable { + private class XmlReport implements AutoCloseable { private final XMLStreamWriter xml; private final ReplacingWriter out; From b5c7f4eeaff5b8a654e9ea6b78227cf90345b0ae Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Wed, 11 Dec 2024 16:58:21 +0100 Subject: [PATCH 20/23] Move #4153 to 5.11.4 release notes (cherry picked from commit d7e765caa23f76cc68c29a674ec6457d6eaa2680) --- .../src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc index ce1fd83e14c4..1c333742dabd 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc @@ -20,6 +20,8 @@ JUnit repository on GitHub. exception messages) in the legacy XML report generated by the Console Launcher. This change ensures the resulting XML files can be processed by downstream tools while preserving whitespace characters. +* Enable auto-flushing of output in the `ConsoleLauncher` to fix issues with buffering, + in particular when using the `--details=testfeed` option. [[release-notes-5.11.4-junit-platform-deprecations-and-breaking-changes]] ==== Deprecations and Breaking Changes From 0444353084f7c47bb29e785b10cf3e835454c2da Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Tue, 3 Dec 2024 09:45:25 +0100 Subject: [PATCH 21/23] Fix Maven integration tests on JDK 24 (cherry picked from commit 35a3fd68f4226f6fe7d4ea42255a999402a07484) --- .../java/platform/tooling/support/tests/MavenEnvVars.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java index bcec609010a0..eebfc4ffd58f 100644 --- a/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java +++ b/platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java @@ -16,9 +16,11 @@ final class MavenEnvVars { - // https://issues.apache.org/jira/browse/MNG-8248 static final Map FOR_JDK24_AND_LATER = JRE.currentVersion().compareTo(JRE.JAVA_24) >= 0 // - ? Map.of("MAVEN_OPTS", "--enable-native-access=ALL-UNNAMED") // + ? Map.of("MAVEN_OPTS", String.join(" ", // + "--enable-native-access=ALL-UNNAMED", // https://issues.apache.org/jira/browse/MNG-8248 + "--sun-misc-unsafe-memory-access=allow" // https://issues.apache.org/jira/browse/MNG-8399 + )) // : Map.of(); private MavenEnvVars() { From d09312174e38e36ec7ac9b35f7033d6a2b693125 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Thu, 12 Dec 2024 08:45:31 +0100 Subject: [PATCH 22/23] Finalize 5.11.4 release notes --- .../release-notes/release-notes-5.11.4.adoc | 39 ++----------------- 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc index 1c333742dabd..b5100622d81e 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc @@ -1,9 +1,9 @@ [[release-notes-5.11.4]] == 5.11.4 -*Date of Release:* ❓ +*Date of Release:* December 13, 2024 -*Scope:* ❓ +*Scope:* Bug fixes and enhancements since 5.11.3 For a complete list of all _closed_ issues and pull requests for this release, consult the link:{junit5-repo}+/milestone/86?closed=1+[5.11.4] milestone page in the @@ -23,30 +23,10 @@ JUnit repository on GitHub. * Enable auto-flushing of output in the `ConsoleLauncher` to fix issues with buffering, in particular when using the `--details=testfeed` option. -[[release-notes-5.11.4-junit-platform-deprecations-and-breaking-changes]] -==== Deprecations and Breaking Changes - -* ❓ - -[[release-notes-5.11.4-junit-platform-new-features-and-improvements]] -==== New Features and Improvements - -* ❓ - [[release-notes-5.11.4-junit-jupiter]] === JUnit Jupiter -[[release-notes-5.11.4-junit-jupiter-bug-fixes]] -==== Bug Fixes - -* ❓ - -[[release-notes-5.11.4-junit-jupiter-deprecations-and-breaking-changes]] -==== Deprecations and Breaking Changes - -* ❓ - [[release-notes-5.11.4-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements @@ -56,17 +36,4 @@ JUnit repository on GitHub. [[release-notes-5.11.4-junit-vintage]] === JUnit Vintage -[[release-notes-5.11.4-junit-vintage-bug-fixes]] -==== Bug Fixes - -* ❓ - -[[release-notes-5.11.4-junit-vintage-deprecations-and-breaking-changes]] -==== Deprecations and Breaking Changes - -* ❓ - -[[release-notes-5.11.4-junit-vintage-new-features-and-improvements]] -==== New Features and Improvements - -* ❓ +No changes. From 6430ba4f653f6ae42f326cd0731b259ee699c719 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Mon, 16 Dec 2024 14:07:03 +0100 Subject: [PATCH 23/23] Release 5.11.4 --- README.md | 2 +- .../docs/asciidoc/release-notes/release-notes-5.11.4.adoc | 2 +- gradle.properties | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fcbe4486edd7..4ba73add4258 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This repository is the home of _JUnit 5_. ## Latest Releases -- General Availability (GA): [JUnit 5.11.3](https://github.com/junit-team/junit5/releases/tag/r5.11.3) (October 21, 2024) +- General Availability (GA): [JUnit 5.11.4](https://github.com/junit-team/junit5/releases/tag/r5.11.4) (December 16, 2024) - Preview (Milestone/Release Candidate): N/A ## Documentation diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc index b5100622d81e..e9ed32bc772a 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.11.4.adoc @@ -1,7 +1,7 @@ [[release-notes-5.11.4]] == 5.11.4 -*Date of Release:* December 13, 2024 +*Date of Release:* December 16, 2024 *Scope:* Bug fixes and enhancements since 5.11.3 diff --git a/gradle.properties b/gradle.properties index b56888f7e0ae..f199b860ccff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,13 +1,13 @@ group = org.junit -version = 5.11.4-SNAPSHOT +version = 5.11.4 jupiterGroup = org.junit.jupiter platformGroup = org.junit.platform -platformVersion = 1.11.4-SNAPSHOT +platformVersion = 1.11.4 vintageGroup = org.junit.vintage -vintageVersion = 5.11.4-SNAPSHOT +vintageVersion = 5.11.4 # We need more metaspace due to apparent memory leak in Asciidoctor/JRuby # The exports are needed due to https://github.com/diffplug/spotless/issues/834