Skip to content

Commit

Permalink
Use ConsoleLauncher's --reports-dir as report output dir
Browse files Browse the repository at this point in the history
Moreover, when an output dir is configured explicitly it's now used
without creating another subdirectory. A new `{uniqueNumber}`
placeholder may be used to create a unique directory per test execution.
This may be used by build tools that create multiple forks to run tests
in parallel.
  • Loading branch information
marcphilipp committed Nov 20, 2024
1 parent b218626 commit f4fa63c
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 77 deletions.
3 changes: 1 addition & 2 deletions documentation/documentation.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ tasks {

val consoleLauncherTestReportsDir = project.layout.buildDirectory.dir("console-launcher-test-results")
val consoleLauncherTestEventXmlFiles =
files(consoleLauncherTestReportsDir.map { it.asFileTree.matching { include("junit-*/open-test-report.xml") } })
files(consoleLauncherTestReportsDir.map { it.asFileTree.matching { include("**/open-test-report.xml") } })

val consoleLauncherTest by registering(RunConsoleLauncher::class) {
args.addAll("execute")
Expand All @@ -157,7 +157,6 @@ tasks {
argumentProviders.add(CommandLineArgumentProvider {
listOf(
"--reports-dir=${consoleLauncherTestReportsDir.get()}",
"--config=junit.platform.reporting.output.dir=${consoleLauncherTestReportsDir.get()}",
)
})
args.addAll("--include-classname", ".*Tests")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import org.gradle.api.tasks.PathSensitivity.NONE
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
import org.gradle.internal.os.OperatingSystem
import java.nio.file.Files

plugins {
`java-library`
Expand Down Expand Up @@ -118,18 +117,20 @@ tasks.withType<Test>().configureEach {
jvmArgumentProviders += CommandLineArgumentProvider {
listOf(
"-Djunit.platform.reporting.open.xml.enabled=true",
"-Djunit.platform.reporting.output.dir=${reports.junitXml.outputLocation.get().asFile.absolutePath}"
"-Djunit.platform.reporting.output.dir=${reports.junitXml.outputLocation.get().asFile.absolutePath}/junit-{uniqueNumber}",
)
}

jvmArgumentProviders += objects.newInstance(JavaAgentArgumentProvider::class).apply {
classpath.from(javaAgentClasspath)
}

val reportFiles = objects.fileTree().from(reports.junitXml.outputLocation).matching { include("junit-*/open-test-report.xml") }
val reportDirTree = objects.fileTree().from(reports.junitXml.outputLocation)
doFirst {
reportFiles.files.forEach {
Files.delete(it.toPath())
reportDirTree.visit {
if (name.startsWith("junit-")) {
file.deleteRecursively()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
package org.junit.platform.console.tasks;

import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.platform.console.tasks.DiscoveryRequestCreator.toDiscoveryRequestBuilder;
import static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_PROPERTY_NAME;

import java.io.PrintWriter;
import java.net.URL;
Expand All @@ -32,6 +34,7 @@
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
Expand Down Expand Up @@ -75,7 +78,7 @@ private void discoverTests(PrintWriter out) {
Launcher launcher = launcherSupplier.get();
Optional<DetailsPrintingListener> commandLineTestPrinter = createDetailsPrintingListener(out);

LauncherDiscoveryRequest discoveryRequest = new DiscoveryRequestCreator().toDiscoveryRequest(discoveryOptions);
LauncherDiscoveryRequest discoveryRequest = toDiscoveryRequestBuilder(discoveryOptions).build();
TestPlan testPlan = launcher.discover(discoveryRequest);

commandLineTestPrinter.ifPresent(printer -> printer.listTests(testPlan));
Expand All @@ -98,8 +101,10 @@ private TestExecutionSummary executeTests(PrintWriter out, Optional<Path> report
Launcher launcher = launcherSupplier.get();
SummaryGeneratingListener summaryListener = registerListeners(out, reportsDir, launcher);

LauncherDiscoveryRequest discoveryRequest = new DiscoveryRequestCreator().toDiscoveryRequest(discoveryOptions);
launcher.execute(discoveryRequest);
LauncherDiscoveryRequestBuilder discoveryRequestBuilder = toDiscoveryRequestBuilder(discoveryOptions);
reportsDir.ifPresent(dir -> discoveryRequestBuilder.configurationParameter(OUTPUT_DIR_PROPERTY_NAME,
dir.toAbsolutePath().toString()));
launcher.execute(discoveryRequestBuilder.build());

TestExecutionSummary summary = summaryListener.getSummary();
if (summary.getTotalFailureCount() > 0 || outputOptions.getDetails() != Details.NONE) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,25 @@
import org.junit.platform.engine.discovery.ClasspathRootSelector;
import org.junit.platform.engine.discovery.IterationSelector;
import org.junit.platform.engine.discovery.MethodSelector;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;

/**
* @since 1.0
*/
class DiscoveryRequestCreator {

LauncherDiscoveryRequest toDiscoveryRequest(TestDiscoveryOptions options) {
static LauncherDiscoveryRequestBuilder toDiscoveryRequestBuilder(TestDiscoveryOptions options) {
LauncherDiscoveryRequestBuilder requestBuilder = request();
List<? extends DiscoverySelector> selectors = createDiscoverySelectors(options);
requestBuilder.selectors(selectors);
addFilters(requestBuilder, options, selectors);
requestBuilder.configurationParameters(options.getConfigurationParameters());
requestBuilder.configurationParametersResources(
options.getConfigurationParametersResources().toArray(new String[0]));
return requestBuilder.build();
return requestBuilder;
}

private List<? extends DiscoverySelector> createDiscoverySelectors(TestDiscoveryOptions options) {
private static List<? extends DiscoverySelector> createDiscoverySelectors(TestDiscoveryOptions options) {
List<DiscoverySelector> explicitSelectors = options.getExplicitSelectors();
if (options.isScanClasspath()) {
Preconditions.condition(explicitSelectors.isEmpty(),
Expand All @@ -77,12 +76,12 @@ private List<? extends DiscoverySelector> createDiscoverySelectors(TestDiscovery
"Please specify an explicit selector option or use --scan-class-path or --scan-modules");
}

private List<ClasspathRootSelector> createClasspathRootSelectors(TestDiscoveryOptions options) {
private static List<ClasspathRootSelector> createClasspathRootSelectors(TestDiscoveryOptions options) {
Set<Path> classpathRoots = determineClasspathRoots(options);
return selectClasspathRoots(classpathRoots);
}

private Set<Path> determineClasspathRoots(TestDiscoveryOptions options) {
private static Set<Path> determineClasspathRoots(TestDiscoveryOptions options) {
if (options.getSelectedClasspathEntries().isEmpty()) {
Set<Path> rootDirs = new LinkedHashSet<>(ReflectionUtils.getAllClasspathRootDirectories());
rootDirs.addAll(options.getExistingAdditionalClasspathEntries());
Expand All @@ -91,7 +90,7 @@ private Set<Path> determineClasspathRoots(TestDiscoveryOptions options) {
return new LinkedHashSet<>(options.getSelectedClasspathEntries());
}

private void addFilters(LauncherDiscoveryRequestBuilder requestBuilder, TestDiscoveryOptions options,
private static void addFilters(LauncherDiscoveryRequestBuilder requestBuilder, TestDiscoveryOptions options,
List<? extends DiscoverySelector> selectors) {
requestBuilder.filters(includedClassNamePatterns(options, selectors));

Expand Down Expand Up @@ -133,7 +132,7 @@ private void addFilters(LauncherDiscoveryRequestBuilder requestBuilder, TestDisc
}
}

private ClassNameFilter includedClassNamePatterns(TestDiscoveryOptions options,
private static ClassNameFilter includedClassNamePatterns(TestDiscoveryOptions options,
List<? extends DiscoverySelector> selectors) {
Stream<String> patternStreams = Stream.concat( //
options.getIncludedClassNamePatterns().stream(), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ public class LauncherConstants {

public static final String OUTPUT_DIR_PROPERTY_NAME = "junit.platform.reporting.output.dir";

public static final String OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER = "{uniqueNumber}";

private LauncherConstants() {
/* no-op */
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,8 @@ private OutputDirectoryProvider getOutputDirectoryProvider(
if (this.outputDirectoryProvider != null) {
return this.outputDirectoryProvider;
}
return new HierarchicalOutputDirectoryProvider(() -> {
OutputDir outputDir = OutputDir.create(configurationParameters.get(OUTPUT_DIR_PROPERTY_NAME));
return outputDir.createDir("junit");
});
return new HierarchicalOutputDirectoryProvider(
() -> OutputDir.create(configurationParameters.get(OUTPUT_DIR_PROPERTY_NAME)).toPath());
}

private LauncherConfigurationParameters buildLauncherConfigurationParameters() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.junit.platform.launcher.listeners;

import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER;

import java.io.IOException;
import java.io.UncheckedIOException;
Expand All @@ -22,6 +23,7 @@
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import org.apiguardian.api.API;
Expand All @@ -30,13 +32,10 @@
@API(status = INTERNAL, since = "1.9")
public class OutputDir {

private final SecureRandom random = new SecureRandom();
private static final Pattern OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER_PATTERN = Pattern.compile(
Pattern.quote(OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER));

public static OutputDir create(Optional<String> customDir) {
return createWithPath(customDir.filter(StringUtils::isNotBlank).map(Paths::get));
}

public static OutputDir createWithPath(Optional<Path> customDir) {
try {
return createSafely(customDir, () -> Paths.get(".").toAbsolutePath());
}
Expand All @@ -48,12 +47,22 @@ public static OutputDir createWithPath(Optional<Path> customDir) {
/**
* Package private for testing purposes.
*/
static OutputDir createSafely(Optional<Path> customDir, Supplier<Path> currentWorkingDir) throws IOException {
static OutputDir createSafely(Optional<String> customDir, Supplier<Path> currentWorkingDir) throws IOException {
return createSafely(customDir, currentWorkingDir, new SecureRandom());
}

private static OutputDir createSafely(Optional<String> customDir, Supplier<Path> currentWorkingDir,
SecureRandom random) throws IOException {
Path cwd = currentWorkingDir.get();
Path outputDir;

if (customDir.isPresent()) {
outputDir = cwd.resolve(customDir.get());
if (customDir.isPresent() && StringUtils.isNotBlank(customDir.get())) {
String customPath = customDir.get();
while (customPath.contains(OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER)) {
customPath = OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER_PATTERN.matcher(customPath) //
.replaceFirst(String.valueOf(Math.abs(random.nextLong())));
}
outputDir = cwd.resolve(customPath);
}
else if (Files.exists(cwd.resolve("pom.xml"))) {
outputDir = cwd.resolve("target");
Expand All @@ -69,31 +78,21 @@ else if (containsFilesWithExtensions(cwd, ".gradle", ".gradle.kts")) {
Files.createDirectories(outputDir);
}

return new OutputDir(outputDir.normalize());
return new OutputDir(outputDir.normalize(), random);
}

private final Path path;
private final SecureRandom random;

private OutputDir(Path path) {
private OutputDir(Path path, SecureRandom random) {
this.path = path;
this.random = random;
}

public Path toPath() {
return path;
}

public Path createDir(String prefix) throws UncheckedIOException {
String filename = String.format("%s-%d", prefix, Math.abs(random.nextLong()));
Path outputFile = path.resolve(filename);

try {
return Files.createDirectory(outputFile);
}
catch (IOException e) {
throw new UncheckedIOException("Failed to create output directory: " + outputFile, e);
}
}

public Path createFile(String prefix, String extension) throws UncheckedIOException {
String filename = String.format("%s-%d.%s", prefix, Math.abs(random.nextLong()), extension);
Path outputFile = path.resolve(filename);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,7 @@ void convertsConfigurationParametersResources() {
}

private LauncherDiscoveryRequest convert() {
var creator = new DiscoveryRequestCreator();
return creator.toDiscoveryRequest(options);
return DiscoveryRequestCreator.toDiscoveryRequestBuilder(options).build();
}

private void assertIncludes(Filter<String> filter, String included) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,77 +10,92 @@

package org.junit.platform.launcher.listeners;

import static org.assertj.core.api.Assertions.as;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.STRING;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.platform.launcher.LauncherConstants;

class OutputDirTests {

@TempDir
Path cwd;

@Test
void getOutputDirUsesCustomOutputDir() throws Exception {
String customDir = "build/UniqueIdTrackingListenerIntegrationTests";
Path outputDir = OutputDir.create(Optional.of(customDir)).toPath();
assertThat(Files.isSameFile(Paths.get(customDir), outputDir)).isTrue();
var customDir = cwd.resolve("custom-dir");
var outputDir = OutputDir.create(Optional.of(customDir.toAbsolutePath().toString())).toPath();
assertThat(Files.isSameFile(customDir, outputDir)).isTrue();
assertThat(outputDir).exists();
}

@Test
void getOutputDirUsesCustomOutputDirWithPlaceholder() {
var customDir = cwd.resolve("build").resolve("junit-" + LauncherConstants.OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER);
var outputDir = OutputDir.create(Optional.of(customDir.toAbsolutePath().toString())).toPath();
assertThat(outputDir).exists() //
.hasParent(cwd.resolve("build")) //
.extracting(it -> it.getFileName().toString(), as(STRING)) //
.matches("junit-\\d+");
}

@Test
void getOutputDirFallsBackToCurrentWorkingDir() throws Exception {
String cwd = "src/test/resources/listeners/uidtracking";
String expected = cwd;
var expected = cwd;

assertOutputDirIsDetected(cwd, expected);
assertOutputDirIsDetected(expected);
}

@Test
void getOutputDirDetectsMavenPom() throws Exception {
String cwd = "src/test/resources/listeners/uidtracking/maven";
String expected = cwd + "/target";
Files.createFile(cwd.resolve("pom.xml"));
var expected = cwd.resolve("target");

assertOutputDirIsDetected(cwd, expected);
assertOutputDirIsDetected(expected);
}

@Test
void getOutputDirDetectsGradleGroovyDefaultBuildScript() throws Exception {
String cwd = "src/test/resources/listeners/uidtracking/gradle/groovy";
String expected = cwd + "/build";
Files.createFile(cwd.resolve("build.gradle"));
var expected = cwd.resolve("build");

assertOutputDirIsDetected(cwd, expected);
assertOutputDirIsDetected(expected);
}

@Test
void getOutputDirDetectsGradleGroovyCustomBuildScript() throws Exception {
String cwd = "src/test/resources/listeners/uidtracking/gradle/groovy/sub-project";
String expected = cwd + "/build";
Files.createFile(cwd.resolve("sub-project.gradle"));
var expected = cwd.resolve("build");

assertOutputDirIsDetected(cwd, expected);
assertOutputDirIsDetected(expected);
}

@Test
void getOutputDirDetectsGradleKotlinDefaultBuildScript() throws Exception {
String cwd = "src/test/resources/listeners/uidtracking/gradle/kotlin";
String expected = cwd + "/build";
Files.createFile(cwd.resolve("build.gradle.kts"));
var expected = cwd.resolve("build");

assertOutputDirIsDetected(cwd, expected);
assertOutputDirIsDetected(expected);
}

@Test
void getOutputDirDetectsGradleKotlinCustomBuildScript() throws Exception {
String cwd = "src/test/resources/listeners/uidtracking/gradle/kotlin/sub-project";
String expected = cwd + "/build";
Files.createFile(cwd.resolve("sub-project.gradle.kts"));
var expected = cwd.resolve("build");

assertOutputDirIsDetected(cwd, expected);
assertOutputDirIsDetected(expected);
}

private void assertOutputDirIsDetected(String cwd, String expected) throws IOException {
Path outputDir = OutputDir.createSafely(Optional.empty(), () -> Paths.get(cwd)).toPath();
assertThat(Files.isSameFile(Paths.get(expected), outputDir)).isTrue();
private void assertOutputDirIsDetected(Path expected) throws IOException {
var outputDir = OutputDir.createSafely(Optional.empty(), () -> cwd).toPath();
assertThat(Files.isSameFile(expected, outputDir)).isTrue();
assertThat(outputDir).exists();
}

Expand Down
Loading

0 comments on commit f4fa63c

Please sign in to comment.