Skip to content

Commit 3b49a06

Browse files
committed
Update this for M2 and M3
1 parent afc2735 commit 3b49a06

File tree

7 files changed

+268
-4
lines changed

7 files changed

+268
-4
lines changed

README.md

Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# JUnit Platform ReportEntry data capture in Gradle
1+
# Demos
22

33
## Milestone 1 Demo
44

@@ -26,7 +26,7 @@ is displayed at the class level in the metadata tab in the test report here:
2626

2727
<img width="652" height="395" alt="image" src="https://github.com/user-attachments/assets/ca234200-8e70-425d-b55b-e9bfea734066" />
2828

29-
Data published during setup/cleanup:
29+
Data published during setup/cleanup:
3030

3131
```java
3232

@@ -85,4 +85,114 @@ Received test metadata event: metadata
8585
Received test metadata event: metadata
8686
afterEach = value4
8787
BUILD SUCCESSFUL in 1s
88-
```
88+
```
89+
90+
## Milestone 2 & 3 Demo
91+
92+
This project demonstrates capturing `ReportEntry` and `FileEntry` data published by JUnit Platform during test execution in Gradle.
93+
94+
This project contains a demonstration project in `/demo-m2-3` which is a JVM library project created using Gradle `init`.
95+
96+
Run the demo project using `./gradlew test` and the tests should pass.
97+
98+
The following files are only created after running `./gradlew test`:
99+
100+
Open the [test report](demo-m2-3/build/reports/tests/test/index.html), you should be able to navigate the test reports to see `FileEntry` data published by the tests appear as "attachments" and `ReportEntry` data published as "data".
101+
102+
The [JUnit XML report](demo-m2-3/build/test-results/test/TEST-org.example.LibraryTest.xml) also includes the same data:
103+
- `ReportEntry` are captured as `<properties/>`
104+
- `FileEntry` are captured as `[[ATTACHMENT|/path/to/file]]` based on [conventions used by Jenkins, Azure pipelines and GitLab](https://kohsuke.org/2012/03/13/attaching-files-to-junit-tests/).
105+
106+
NOTE: Make sure you open the HTML report in the browser directly as a local file and not through the IDE's built-in webserver.
107+
108+
### Details
109+
110+
The test class is located at [`LibraryTest.java`](demo-m2-3/src/test/java/org/example/LibraryTest.java).
111+
112+
Data published during test construction:
113+
```java
114+
LibraryTest(TestReporter testReporter) {
115+
testReporter.publishEntry("constructor", "value1");
116+
// Publish a file at the class level
117+
testReporter.publishFile("constructor.json", MediaType.APPLICATION_JSON, path -> {
118+
Files.writeString(path, "{ constructor: [] }");
119+
});
120+
}
121+
```
122+
is displayed at the class level in the data and attachments tabs.
123+
124+
Data published during setup/cleanup:
125+
126+
```java
127+
@BeforeEach
128+
public void beforeEach(TestReporter testReporter) {
129+
testReporter.publishEntry("beforeEach", "value2");
130+
// Publish a text file before the test
131+
testReporter.publishFile("beforeEach.txt", MediaType.TEXT_PLAIN, path -> {
132+
Files.writeString(path, "Hello world");
133+
});
134+
}
135+
136+
@AfterEach
137+
public void afterEach(TestReporter testReporter) {
138+
testReporter.publishEntry("afterEach", "value4");
139+
// Publish a (fake) video file after the test
140+
testReporter.publishFile("afterEach.mp4", MediaType.create("video", "mp4"), path -> {
141+
Files.writeString(path, "This is just a text file that pretends to be a video.");
142+
});
143+
}
144+
```
145+
146+
and during test execution:
147+
148+
```java
149+
@Test
150+
void someLibraryMethodReturnsTrue(TestReporter testReporter) {
151+
testReporter.publishEntry("test", "value3");
152+
// Publish an image during the test
153+
testReporter.publishFile("test.svg", MediaType.create("image", "svg+xml"), path -> {
154+
Files.writeString(path, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 800 600\"><rect width=\"800\" height=\"600\" fill=\"#F5F5F0\"/><rect width=\"350\" height=\"600\" fill=\"#D4292E\"/><rect x=\"350\" width=\"450\" height=\"250\" fill=\"#1356A3\"/><rect x=\"350\" y=\"250\" width=\"200\" height=\"350\" fill=\"#F7D838\"/><rect x=\"550\" y=\"250\" width=\"250\" height=\"350\" fill=\"#F5F5F0\"/><line x1=\"350\" y1=\"0\" x2=\"350\" y2=\"600\" stroke=\"#1A1A1A\" stroke-width=\"12\"/><line x1=\"350\" y1=\"250\" x2=\"800\" y2=\"250\" stroke=\"#1A1A1A\" stroke-width=\"12\"/><line x1=\"550\" y1=\"250\" x2=\"550\" y2=\"600\" stroke=\"#1A1A1A\" stroke-width=\"12\"/><rect width=\"800\" height=\"600\" fill=\"none\" stroke=\"#1A1A1A\" stroke-width=\"14\"/></svg>");
155+
});
156+
157+
Library classUnderTest = new Library();
158+
assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'");
159+
}
160+
```
161+
162+
is displayed at the method level in the data and attachments tabs.
163+
164+
The report renders image and videos in the report and all other files are linked to. The order of attachments is based on the order they are published.
165+
166+
## Milestone 2 & 3 Tooling API Demo
167+
168+
This project demonstrates capturing `ReportEntry` and `FileEntry` data published by JUnit Platform during test execution in Gradle when running via the Tooling API.
169+
170+
This project contains a demonstration project in `/demo-m2-3-tapi` which is a JVM application project that runs the `/demo-m2-3` build using the Tooling API, and listens for `TestMetadataEvent`s while doing so.
171+
172+
Run the demo project using `./gradlew :demon-m2-3-tapi:run` and the build should succeed.
173+
You will see the data and attachments published by the test printed to the console using metadata events:
174+
175+
```text
176+
> Task :demo-m2-3-tapi:run
177+
> Task :demo-m2-3:processTestResources NO-SOURCE
178+
> Task :demo-m2-3:processResources NO-SOURCE
179+
> Task :demo-m2-3:compileJava UP-TO-DATE
180+
> Task :demo-m2-3:classes UP-TO-DATE
181+
> Task :demo-m2-3:compileTestJava UP-TO-DATE
182+
> Task :demo-m2-3:testClasses UP-TO-DATE
183+
Received test metadata event: metadata
184+
File attachment (application/json):
185+
/Users/sterling/gits/sample-rbt-engine/demo-m2-3/build/junit-jupiter/org.example.LibraryTest/someLibraryMethodReturnsTrue(org.junit.jupiter.api.TestReporter)/constructor.json
186+
Received test metadata event: metadata
187+
File attachment (text/plain):
188+
/Users/sterling/gits/sample-rbt-engine/demo-m2-3/build/junit-jupiter/org.example.LibraryTest/someLibraryMethodReturnsTrue(org.junit.jupiter.api.TestReporter)/beforeEach.txt
189+
Received test metadata event: metadata
190+
File attachment (image/svg+xml):
191+
/Users/sterling/gits/sample-rbt-engine/demo-m2-3/build/junit-jupiter/org.example.LibraryTest/someLibraryMethodReturnsTrue(org.junit.jupiter.api.TestReporter)/test.svg
192+
Received test metadata event: metadata
193+
File attachment (video/mp4):
194+
/Users/sterling/gits/sample-rbt-engine/demo-m2-3/build/junit-jupiter/org.example.LibraryTest/someLibraryMethodReturnsTrue(org.junit.jupiter.api.TestReporter)/afterEach.mp4
195+
> Task :demo-m2-3:test
196+
197+
BUILD SUCCESSFUL in 2s
198+
```

demo-m2-3-tapi/build.gradle.kts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
plugins {
4+
application
5+
}
6+
7+
dependencies {
8+
implementation(gradleApi())
9+
}
10+
11+
application {
12+
mainClass = "org.example.Main"
13+
}
14+
15+
testing {
16+
suites {
17+
named<JvmTestSuite>("test") {
18+
useJUnitJupiter()
19+
}
20+
}
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.example;
2+
3+
import java.io.File;
4+
import java.util.Set;
5+
6+
import org.gradle.tooling.BuildLauncher;
7+
import org.gradle.tooling.GradleConnector;
8+
import org.gradle.tooling.ProjectConnection;
9+
import org.gradle.tooling.events.OperationType;
10+
import org.gradle.tooling.events.test.*;
11+
12+
@SuppressWarnings("UnstableApiUsage")
13+
public class Main {
14+
public static void main(String[] args) {
15+
GradleConnector connector = GradleConnector.newConnector();
16+
connector.forProjectDirectory(new File("../demo-m2-3"));
17+
18+
try (ProjectConnection connection = connector.connect()) {
19+
// Configure the launcher to listen and print test metadata events
20+
BuildLauncher launcher = connection.newBuild();
21+
launcher.addProgressListener(progressEvent -> {
22+
if (progressEvent instanceof TestMetadataEvent testMetadataEvent) {
23+
System.out.println("Received test metadata event: " + testMetadataEvent.getDisplayName());
24+
if (!testMetadataEvent.getValues().isEmpty()) {
25+
testMetadataEvent.getValues().forEach((key, value) -> System.out.println(" " + key + " = " + value));
26+
} else {
27+
FileAttachment fileAttachment = testMetadataEvent.get(FileAttachment.class);
28+
if (fileAttachment!=null) {
29+
System.out.printf("File attachment (%s):%n\t%s%n", fileAttachment.getMediaType(), fileAttachment.getPath());
30+
} else {
31+
System.out.println("Unknown type of data");
32+
}
33+
}
34+
35+
}
36+
}, Set.of(OperationType.TEST, OperationType.TEST_METADATA));
37+
38+
// Run the demo-m2-3 build, rerunning tests to ensure they aren't found to be up-to-date
39+
launcher.forTasks("test", "--rerun");
40+
launcher.setStandardOutput(System.out);
41+
launcher.setStandardError(System.err);
42+
launcher.run();
43+
}
44+
}
45+
}

demo-m2-3/build.gradle.kts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
plugins {
4+
`java-library`
5+
}
6+
7+
java {
8+
sourceCompatibility = JavaVersion.VERSION_17
9+
targetCompatibility = JavaVersion.VERSION_17
10+
11+
toolchain {
12+
languageVersion = JavaLanguageVersion.of(17)
13+
}
14+
}
15+
16+
dependencies {
17+
api(libs.commons.math3)
18+
implementation(libs.guava)
19+
}
20+
21+
testing {
22+
suites {
23+
named<JvmTestSuite>("test") {
24+
useJUnitJupiter()
25+
}
26+
}
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* This source file was generated by the Gradle 'init' task
3+
*/
4+
package org.example;
5+
6+
public class Library {
7+
public boolean someLibraryMethod() {
8+
return true;
9+
}
10+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* This source file was generated by the Gradle 'init' task
3+
*/
4+
package org.example;
5+
6+
import org.junit.jupiter.api.*;
7+
import org.junit.jupiter.api.extension.MediaType;
8+
9+
import java.nio.file.Files;
10+
11+
import static org.junit.jupiter.api.Assertions.*;
12+
13+
class LibraryTest {
14+
LibraryTest(TestReporter testReporter) {
15+
testReporter.publishEntry("constructor", "value1");
16+
// Publish a file at the class level
17+
testReporter.publishFile("constructor.json", MediaType.APPLICATION_JSON, path -> {
18+
Files.writeString(path, "{ constructor: [] }");
19+
});
20+
}
21+
22+
@BeforeEach
23+
public void beforeEach(TestReporter testReporter) {
24+
testReporter.publishEntry("beforeEach", "value2");
25+
// Publish a text file before the test
26+
testReporter.publishFile("beforeEach.txt", MediaType.TEXT_PLAIN, path -> {
27+
Files.writeString(path, "Hello world");
28+
});
29+
}
30+
31+
@Test
32+
void someLibraryMethodReturnsTrue(TestReporter testReporter) {
33+
testReporter.publishEntry("test", "value3");
34+
// Publish an image during the test
35+
testReporter.publishFile("test.svg", MediaType.create("image", "svg+xml"), path -> {
36+
Files.writeString(path, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 800 600\"><rect width=\"800\" height=\"600\" fill=\"#F5F5F0\"/><rect width=\"350\" height=\"600\" fill=\"#D4292E\"/><rect x=\"350\" width=\"450\" height=\"250\" fill=\"#1356A3\"/><rect x=\"350\" y=\"250\" width=\"200\" height=\"350\" fill=\"#F7D838\"/><rect x=\"550\" y=\"250\" width=\"250\" height=\"350\" fill=\"#F5F5F0\"/><line x1=\"350\" y1=\"0\" x2=\"350\" y2=\"600\" stroke=\"#1A1A1A\" stroke-width=\"12\"/><line x1=\"350\" y1=\"250\" x2=\"800\" y2=\"250\" stroke=\"#1A1A1A\" stroke-width=\"12\"/><line x1=\"550\" y1=\"250\" x2=\"550\" y2=\"600\" stroke=\"#1A1A1A\" stroke-width=\"12\"/><rect width=\"800\" height=\"600\" fill=\"none\" stroke=\"#1A1A1A\" stroke-width=\"14\"/></svg>");
37+
});
38+
39+
Library classUnderTest = new Library();
40+
assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'");
41+
}
42+
43+
@AfterEach
44+
public void afterEach(TestReporter testReporter) {
45+
testReporter.publishEntry("afterEach", "value4");
46+
// Publish a (fake) video file after the test
47+
testReporter.publishFile("afterEach.mp4", MediaType.create("video", "mp4"), path -> {
48+
Files.writeString(path, "This is just a text file that pretends to be a video.");
49+
});
50+
}
51+
}

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ plugins {
1212
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
1313
}
1414

15-
include("demo-m1", "demo-m1-tapi")
15+
include("demo-m2-3", "demo-m2-3-tapi")
1616

1717
rootProject.name = "sample-rbt-engine"

0 commit comments

Comments
 (0)