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 @@ -28,6 +28,9 @@ repository on GitHub.

* Support for creating a `ModuleSelector` from a `java.lang.Module` and using
its classloader for test discovery.
* `OpenTestReportGeneratingListener` now supports redirecting XML events to a socket via
the new `junit.platform.reporting.open.xml.socket` configuration parameter. When set to a
port number, events are sent to `127.0.0.1:<port>` instead of being written to a file.


[[release-notes-6.1.0-M1-junit-jupiter]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,15 @@ The listener is auto-registered and can be configured via the following
Enable/disable writing the report; defaults to `false`.
`junit.platform.reporting.open.xml.git.enabled=true|false`::
Enable/disable including information about the Git repository (see https://github.com/ota4j-team/open-test-reporting#git[Git extension schema] of open-test-reporting); defaults to `false`.
`junit.platform.reporting.open.xml.socket=<port>`::
Optional port number to redirect events to a socket instead of a file. When specified, the
listener will connect to `127.0.0.1:<port>` and send the XML events to the socket. The socket
connection is automatically closed when the test execution completes.

If enabled, the listener creates an XML report file named `open-test-report.xml` in the
configured <<junit-platform-reporting-output-directory, output directory>>.
configured <<junit-platform-reporting-output-directory, output directory>>, unless the
`junit.platform.reporting.open.xml.socket` configuration parameter is set, in which case the
events are sent to the specified socket instead.

If <<running-tests-capturing-output, output capturing>> is enabled, the captured output
written to `System.out` and `System.err` will be included in the report as well.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,13 @@
import static org.opentest4j.reporting.events.root.RootFactory.started;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Instant;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -102,6 +106,7 @@ public class OpenTestReportGeneratingListener implements TestExecutionListener {

static final String ENABLED_PROPERTY_NAME = "junit.platform.reporting.open.xml.enabled";
static final String GIT_ENABLED_PROPERTY_NAME = "junit.platform.reporting.open.xml.git.enabled";
static final String SOCKET_PROPERTY_NAME = "junit.platform.reporting.open.xml.socket";

private final AtomicInteger idCounter = new AtomicInteger();
private final Map<UniqueId, String> inProgressIds = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -130,17 +135,40 @@ public void testPlanExecutionStarted(TestPlan testPlan) {
.add("junit", JUnitFactory.NAMESPACE, "https://schemas.junit.org/open-test-reporting/junit-1.9.xsd") //
.build();
outputDir = testPlan.getOutputDirectoryCreator().getRootDirectory();
Path eventsXml = outputDir.resolve("open-test-report.xml");
try {
eventsFileWriter = Events.createDocumentWriter(namespaceRegistry, eventsXml);
eventsFileWriter = createDocumentWriter(config, namespaceRegistry);
reportInfrastructure(config);
}
catch (Exception e) {
throw new JUnitException("Failed to initialize XML events file: " + eventsXml, e);
throw new JUnitException("Failed to initialize XML events writer", e);
}
}
}

private DocumentWriter<Events> createDocumentWriter(ConfigurationParameters config,
NamespaceRegistry namespaceRegistry) throws Exception {
return config.get(SOCKET_PROPERTY_NAME, Integer::valueOf) //
.map(port -> {
try {
Socket socket = new Socket(InetAddress.getLoopbackAddress(), port);
Writer writer = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8);
return Events.createDocumentWriter(namespaceRegistry, writer);
}
catch (Exception e) {
throw new JUnitException("Failed to connect to socket on port " + port, e);
}
}) //
.orElseGet(() -> {
try {
Path eventsXml = requireNonNull(outputDir).resolve("open-test-report.xml");
return Events.createDocumentWriter(namespaceRegistry, eventsXml);
}
catch (Exception e) {
throw new JUnitException("Failed to create XML events file", e);
}
});
}

private boolean isEnabled(ConfigurationParameters config) {
return config.getBoolean(ENABLED_PROPERTY_NAME).orElse(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@
import static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;
import static org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener.ENABLED_PROPERTY_NAME;
import static org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener.GIT_ENABLED_PROPERTY_NAME;
import static org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener.SOCKET_PROPERTY_NAME;
import static org.junit.platform.reporting.testutil.FileUtils.findPath;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Map;

import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -265,6 +273,90 @@ void stripsCredentialsFromOriginUrl(String configuredUrl, String reportedUrl, @T
.isEqualTo(reportedUrl);
}

@Test
void writesXmlReportToSocket(@TempDir Path tempDirectory) throws Exception {
var engine = new DemoHierarchicalTestEngine("dummy");
engine.addTest("test1", "Test 1", (context, descriptor) -> {
// Simple test
});

// Start a server socket to receive the XML
var builder = new StringBuilder();

try (var serverSocket = new ServerSocket(0, 50, InetAddress.getLoopbackAddress())) { // Use any available port
int port = serverSocket.getLocalPort();

// Start a daemon thread to accept the connection and read the XML
Thread serverThread = new Thread(() -> {
try (Socket clientSocket = serverSocket.accept();
var reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
}
}
catch (Exception e) {
fail(e);
}
});
serverThread.setDaemon(true);
serverThread.start();

// Execute tests with socket configuration
executeTests(tempDirectory, engine, tempDirectory.resolve("junit-reports"),
Map.of(SOCKET_PROPERTY_NAME, String.valueOf(port)));

// Wait for the server to receive the data
assertThat(serverThread.join(Duration.ofSeconds(10))).isTrue();

// Verify XML was received
var expected = """
<e:events xmlns="https://schemas.opentest4j.org/reporting/core/0.2.0"
xmlns:e="https://schemas.opentest4j.org/reporting/events/0.2.0"
xmlns:git="https://schemas.opentest4j.org/reporting/git/0.2.0"
xmlns:java="https://schemas.opentest4j.org/reporting/java/0.2.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://schemas.junit.org/open-test-reporting/junit-1.9.xsd">
<infrastructure>
<hostName>${xmlunit.ignore}</hostName>
<userName>${xmlunit.ignore}</userName>
<operatingSystem>${xmlunit.ignore}</operatingSystem>
<cpuCores>${xmlunit.ignore}</cpuCores>
<java:javaVersion>${xmlunit.ignore}</java:javaVersion>
<java:fileEncoding>${xmlunit.ignore}</java:fileEncoding>
<java:heapSize max="${xmlunit.isNumber}"/>
</infrastructure>
<e:started id="1" name="dummy" time="${xmlunit.isDateTime}">
<metadata>
<junit:uniqueId>[engine:dummy]</junit:uniqueId>
<junit:legacyReportingName>dummy</junit:legacyReportingName>
<junit:type>CONTAINER</junit:type>
</metadata>
</e:started>
<e:started id="2" name="Test 1" parentId="1" time="${xmlunit.isDateTime}">
<metadata>
<junit:uniqueId>[engine:dummy]/[test:test1]</junit:uniqueId>
<junit:legacyReportingName>Test 1</junit:legacyReportingName>
<junit:type>TEST</junit:type>
</metadata>
</e:started>
<e:finished id="2" time="${xmlunit.isDateTime}">
<result status="SUCCESSFUL"/>
</e:finished>
<e:finished id="1" time="${xmlunit.isDateTime}">
<result status="SUCCESSFUL"/>
</e:finished>
</e:events>
""";
XmlAssert.assertThat(builder.toString()).and(expected) //
.withDifferenceEvaluator(new PlaceholderDifferenceEvaluator()) //
.ignoreWhitespace() //
.areIdentical();
}
}

private static XmlAssert assertThatXml(Path xmlFile) {
return XmlAssert.assertThat(xmlFile) //
.withNamespaceContext(NAMESPACE_CONTEXT);
Expand Down