Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ JUnit repository on GitHub.
`--select-file` and `--select-resource`.
* `ConsoleLauncher` now accepts multiple values for all `--select` options.
* Add `--select-unique-id` support to ConsoleLauncher.
* The `junit-platform-reporting` module now contributes a section containing
JUnit-specific metadata about each test/container to the HTML report written by
open-test-reporting when added to the classpath/module path.
* The following improvements have been made to the open-test-reporting XML output:
- Information about the Git repository, the current branch, the commit hash, and the
current worktree status are now included in the XML report, if applicable.
- A section containing JUnit-specific metadata about each test/container to the HTML
report is now written by open-test-reporting when added to the classpath/module path


[[release-notes-5.12.0-M1-junit-jupiter]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.junit.platform.reporting.open.xml;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;
import static org.junit.platform.commons.util.StringUtils.isNotBlank;
import static org.junit.platform.reporting.open.xml.JUnitFactory.legacyReportingName;
import static org.junit.platform.reporting.open.xml.JUnitFactory.type;
import static org.junit.platform.reporting.open.xml.JUnitFactory.uniqueId;
Expand All @@ -30,6 +31,10 @@
import static org.opentest4j.reporting.events.core.CoreFactory.tags;
import static org.opentest4j.reporting.events.core.CoreFactory.uriSource;
import static org.opentest4j.reporting.events.core.CoreFactory.userName;
import static org.opentest4j.reporting.events.git.GitFactory.branch;
import static org.opentest4j.reporting.events.git.GitFactory.commit;
import static org.opentest4j.reporting.events.git.GitFactory.repository;
import static org.opentest4j.reporting.events.git.GitFactory.status;
import static org.opentest4j.reporting.events.java.JavaFactory.classSource;
import static org.opentest4j.reporting.events.java.JavaFactory.classpathResourceSource;
import static org.opentest4j.reporting.events.java.JavaFactory.fileEncoding;
Expand All @@ -42,18 +47,27 @@
import static org.opentest4j.reporting.events.root.RootFactory.reported;
import static org.opentest4j.reporting.events.root.RootFactory.started;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;

import org.apiguardian.api.API;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.util.ExceptionUtils;
import org.junit.platform.commons.util.StringUtils;
import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.TestExecutionResult;
Expand All @@ -74,6 +88,7 @@
import org.junit.platform.launcher.listeners.OutputDir;
import org.opentest4j.reporting.events.api.DocumentWriter;
import org.opentest4j.reporting.events.api.NamespaceRegistry;
import org.opentest4j.reporting.events.core.Infrastructure;
import org.opentest4j.reporting.events.core.Result;
import org.opentest4j.reporting.events.core.Sources;
import org.opentest4j.reporting.events.root.Events;
Expand Down Expand Up @@ -103,6 +118,7 @@ public void testPlanExecutionStarted(TestPlan testPlan) {
if (isEnabled(config)) {
NamespaceRegistry namespaceRegistry = NamespaceRegistry.builder(Namespace.REPORTING_CORE) //
.add("e", Namespace.REPORTING_EVENTS) //
.add("git", Namespace.REPORTING_GIT) //
.add("java", Namespace.REPORTING_JAVA) //
.add("junit", JUnitFactory.NAMESPACE,
"https://junit.org/junit5/schemas/open-test-reporting/junit-1.9.xsd") //
Expand Down Expand Up @@ -138,9 +154,92 @@ private void reportInfrastructure() {
.append(javaVersion(System.getProperty("java.version"))) //
.append(fileEncoding(System.getProperty("file.encoding"))) //
.append(heapSize(), heapSize -> heapSize.withMax(Runtime.getRuntime().maxMemory()));

addGitInfo(infrastructure);
});
}

private static void addGitInfo(Infrastructure infrastructure) {
boolean gitInstalled = exec("git", "--version").isPresent();
if (gitInstalled) {
exec("git", "config", "--get", "remote.origin.url") //
.filter(StringUtils::isNotBlank) //
.ifPresent(
gitUrl -> infrastructure.append(repository(), repository -> repository.withOriginUrl(gitUrl)));
exec("git", "rev-parse", "--abbrev-ref", "HEAD") //
.filter(StringUtils::isNotBlank) //
.ifPresent(branch -> infrastructure.append(branch(branch)));
exec("git", "rev-parse", "--verify", "HEAD") //
.filter(StringUtils::isNotBlank) //
.ifPresent(gitCommitHash -> infrastructure.append(commit(gitCommitHash)));
exec("git", "status", "--porcelain") //
.ifPresent(statusOutput -> infrastructure.append(status(statusOutput),
status -> status.withClean(statusOutput.isEmpty())));
}
}

static Optional<String> exec(String... args) {

Process process = startProcess(args);

try (Reader out = newBufferedReader(process.getInputStream());
Reader err = newBufferedReader(process.getErrorStream())) {

StringBuilder output = new StringBuilder();
readAllChars(out, (chars, numChars) -> output.append(chars, 0, numChars));

readAllChars(err, (__, ___) -> {
// ignore
});

boolean terminated = process.waitFor(10, TimeUnit.SECONDS);
return terminated && process.exitValue() == 0 ? Optional.of(trimAtEnd(output)) : Optional.empty();
}
catch (InterruptedException e) {
throw ExceptionUtils.throwAsUncheckedException(e);
}
catch (IOException ignore) {
return Optional.empty();
}
finally {
process.destroyForcibly();
}
}

private static BufferedReader newBufferedReader(InputStream stream) {
return new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset()));
}

private static Process startProcess(String[] args) {
Process process;
try {
process = Runtime.getRuntime().exec(args);
}
catch (IOException e) {
throw new UncheckedIOException("Failed to start process", e);
}
return process;
}

private static void readAllChars(Reader reader, BiConsumer<char[], Integer> consumer) throws IOException {
char[] buffer = new char[1024];
int numChars;
while ((numChars = reader.read(buffer)) != -1) {
consumer.accept(buffer, numChars);
}
}

private static String trimAtEnd(StringBuilder value) {
int endIndex = value.length();
for (int i = value.length() - 1; i >= 0; i--) {
if (Character.isWhitespace(value.charAt(i))) {
endIndex--;
break;
}
}
return value.substring(0, endIndex);
}

@Override
public void testPlanExecutionFinished(TestPlan testPlan) {
try {
Expand All @@ -160,7 +259,7 @@ public void executionSkipped(TestIdentifier testIdentifier, String reason) {
reportStarted(testIdentifier, id);
eventsFileWriter.append(finished(id, Instant.now()), //
finished -> finished.append(result(Result.Status.SKIPPED), result -> {
if (StringUtils.isNotBlank(reason)) {
if (isNotBlank(reason)) {
result.append(reason(reason));
}
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ void writesValidXmlReport() throws Exception {
assertThat(validate(xmlFile)).isEmpty();

var expected = """
<e:events xmlns="https://schemas.opentest4j.org/reporting/core/0.1.0" xmlns:e="https://schemas.opentest4j.org/reporting/events/0.1.0" xmlns:java="https://schemas.opentest4j.org/reporting/java/0.1.0"
xmlns:junit="https://schemas.junit.org/open-test-reporting" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<e:events xmlns="https://schemas.opentest4j.org/reporting/core/0.1.0" xmlns:e="https://schemas.opentest4j.org/reporting/events/0.1.0"
xmlns:git="https://schemas.opentest4j.org/reporting/git/0.1.0"
xmlns:java="https://schemas.opentest4j.org/reporting/java/0.1.0"
xmlns:junit="https://schemas.junit.org/open-test-reporting"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://schemas.junit.org/open-test-reporting https://junit.org/junit5/schemas/open-test-reporting/junit-1.9.xsd">
<infrastructure>
<hostName>${xmlunit.ignore}</hostName>
Expand All @@ -75,6 +78,10 @@ void writesValidXmlReport() throws Exception {
<java:javaVersion>${xmlunit.ignore}</java:javaVersion>
<java:fileEncoding>${xmlunit.ignore}</java:fileEncoding>
<java:heapSize max="${xmlunit.isNumber}"/>
<git:repository originUrl="${xmlunit.matchesRegex(https://.+)}"/>
<git:branch>${xmlunit.ignore}</git:branch>
<git:commit>${xmlunit.matchesRegex([0-9a-f]{40})}</git:commit>
<git:status clean="${xmlunit.ignore}">${xmlunit.ignore}</git:status>
</infrastructure>
<e:started id="1" name="dummy" time="${xmlunit.isDateTime}">
<metadata>
Expand Down
2 changes: 2 additions & 0 deletions platform-tooling-support-tests/projects/java-versions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@
<url>file://${maven.repo}</url>
<snapshots>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</releases>
</repository>
</repositories>
Expand Down
6 changes: 4 additions & 2 deletions platform-tooling-support-tests/projects/maven-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,16 @@
<url>file://${maven.repo}</url>
<snapshots>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</releases>
</repository>
<repository>
<id>sonatype-oss-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<id>snapshots-repo</id>
<url>${snapshot.repo.url}</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@
<url>file://${maven.repo}</url>
<snapshots>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</releases>
</repository>
</repositories>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,16 @@
<url>file://${maven.repo}</url>
<snapshots>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</releases>
</repository>
<repository>
<id>sonatype-oss-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<id>snapshots-repo</id>
<url>${snapshot.repo.url}</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
Expand Down
2 changes: 2 additions & 0 deletions platform-tooling-support-tests/projects/vintage/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@
<url>file://${maven.repo}</url>
<snapshots>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</snapshots>
<releases>
<enabled>true</enabled>
<checksumPolicy>ignore</checksumPolicy>
</releases>
</repository>
</repositories>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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 platform.tooling.support.tests;

import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.junit.platform.commons.support.ReflectionSupport.streamFields;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
import org.junit.platform.commons.support.AnnotationSupport;
import org.junit.platform.commons.support.HierarchyTraversalMode;
import org.junit.platform.commons.support.ReflectionSupport;
import org.junit.platform.commons.util.Preconditions;

@Target({ ElementType.PARAMETER, ElementType.FIELD })
@Retention(RUNTIME)
@ExtendWith(GlobalResource.Extension.class)
public @interface GlobalResource {

class Extension implements ParameterResolver, TestInstancePostProcessor {

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return parameterContext.isAnnotated(GlobalResource.class);
}

@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
Class<?> type = parameterContext.getParameter().getType();
return getOrCreateResource(extensionContext, type).get();
}

@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext extensionContext) {
streamFields(testInstance.getClass(), field -> AnnotationSupport.isAnnotated(field, GlobalResource.class),
HierarchyTraversalMode.BOTTOM_UP) //
.forEach(field -> {
try {
field.set(testInstance, getOrCreateResource(extensionContext, field.getType()).get());
}
catch (IllegalAccessException e) {
throw new RuntimeException("Failed to inject resource into field: " + field, e);
}
});
}

@SuppressWarnings("unchecked")
private <T> Resource<T> getOrCreateResource(ExtensionContext extensionContext, Class<T> type) {
return extensionContext.getRoot().getStore(Namespace.GLOBAL) //
.getOrComputeIfAbsent(type, Resource::new, Resource.class);
}
}

class Resource<T> implements CloseableResource {

private final T value;

private Resource(Class<T> type) {
Preconditions.condition(AutoCloseable.class.isAssignableFrom(type),
() -> "Resource type must implement AutoCloseable: " + type.getName());
this.value = ReflectionSupport.newInstance(type);
}

private T get() {
return value;
}

@Override
public void close() throws Throwable {
((AutoCloseable) value).close();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
*/
class JavaVersionsTests {

@GlobalResource
LocalMavenRepo localMavenRepo;

@Test
void java_8() {
var java8Home = Helper.getJavaHome("8");
Expand All @@ -54,7 +57,7 @@ List<String> execute(String version, Path javaHome, Map<String, String> environm
.setTool(Request.maven()) //
.setProject(Projects.JAVA_VERSIONS) //
.setWorkspace("java-versions-" + version) //
.addArguments("-Dmaven.repo=" + MavenRepo.dir()) //
.addArguments(localMavenRepo.toCliArgument(), "-Dmaven.repo=" + MavenRepo.dir()) //
.addArguments("--update-snapshots", "--batch-mode", "verify") //
.setTimeout(TOOL_TIMEOUT) //
.setJavaHome(javaHome);
Expand Down
Loading