Skip to content

Move JFR functionality into junit-platform-launcher #4674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 26, 2025
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
1 change: 0 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ val platformProjects by extra(listOf(
projects.junitPlatformConsole,
projects.junitPlatformConsoleStandalone,
projects.junitPlatformEngine,
projects.junitPlatformJfr,
projects.junitPlatformLauncher,
projects.junitPlatformReporting,
projects.junitPlatformSuite,
Expand Down
6 changes: 0 additions & 6 deletions documentation/documentation.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@ dependencies {
because("Jimfs is used in src/test/java")
}

if (java.toolchain.implementation.orNull == JvmImplementation.J9) {
testRuntimeOnly(libs.jfrPolyfill) {
because("OpenJ9 does not include JFR")
}
}

standaloneConsoleLauncher(projects.junitPlatformConsoleStandalone)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ repository on GitHub.
`--h` (rather than `-h`) or `-help` (rather than `--help`).
* The `junit-platform-runner` module that provided the JUnit 4 based `JUnitPlatform`
runner has been discontinued.
* The `junit-platform-jfr` module that provided custom Java Flight Recorder (JFR) events
for test discovery and execution has been discontinued. Instead, the functionality is
now available directly in `junit-platform-launcher` without requiring an additional
dependency.
* Support for Maven Surefire/Failsafe versions less than 3.0.0 has been dropped.
* The following deprecated APIs have been removed:
- `ReflectionSupport.loadClass(String)` method
Expand Down
4 changes: 0 additions & 4 deletions documentation/src/docs/asciidoc/user-guide/appendix.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@ Please refer to the corresponding sections for <<running-tests-build-maven-bom,
directory. See <<running-tests-console-launcher>> for details.
`junit-platform-engine`::
Public API for test engines. See <<launcher-api-engines-custom>> for details.
`junit-platform-jfr`::
Provides a `LauncherDiscoveryListener` and `TestExecutionListener` for Java Flight
Recorder events on the JUnit Platform. See <<running-tests-listeners-flight-recorder>>
for details.
`junit-platform-launcher`::
Public API for configuring and launching test plans -- typically used by IDEs and
build tools. See <<launcher-api>> for details.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1119,9 +1119,7 @@ to a problem.
In order to record Flight Recorder events generated while running tests, you need to:

1. Ensure that you are using either Java 8 Update 262 or higher or Java 11 or later.
2. Provide the `org.junit.platform.jfr` module (`junit-platform-jfr-{version}.jar`)
on the class-path or module-path at test runtime.
3. Start flight recording when launching a test run. Flight Recorder can be started via
2. Start flight recording when launching a test run. Flight Recorder can be started via
java command line option:

-XX:StartFlightRecording:filename=...
Expand Down
1 change: 0 additions & 1 deletion documentation/src/plantuml/component-diagram.puml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package org.junit.platform {
[junit-platform-commons] as commons
[junit-platform-console] as console
[junit-platform-engine] as engine
[junit-platform-jfr] as jfr
[junit-platform-launcher] as launcher
[junit-platform-reporting] as reporting
[junit-platform-suite] as suite
Expand Down
1 change: 0 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ groovy2-bom = { module = "org.codehaus.groovy:groovy-bom", version = "2.5.23" }
hamcrest = { module = "org.hamcrest:hamcrest", version = "3.0" }
jackson-dataformat-yaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", version.ref = "jackson" }
jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson" }
jfrPolyfill = { module = "org.gradle.jfr.polyfill:jfr-polyfill", version = "1.0.2" }
jfrunit = { module = "org.moditect.jfrunit:jfrunit-core", version = "1.0.0.Alpha2" }
jimfs = { module = "com.google.jimfs:jimfs", version = "1.3.0" }
jmh-core = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,9 @@ dependencies {
testImplementation(project(":junit-jupiter"))

testRuntimeOnly(project(":junit-platform-engine"))
testRuntimeOnly(project(":junit-platform-jfr"))
testRuntimeOnly(project(":junit-platform-reporting"))

testRuntimeOnly(bundleFromLibs("log4j"))
testRuntimeOnly(dependencyFromLibs("jfrPolyfill")) {
because("OpenJ9 does not include JFR")
}
testRuntimeOnly(dependencyFromLibs("openTestReporting-events")) {
because("it's required to run tests via IntelliJ which does not consumed the shadowed jar of junit-platform-reporting")
}
Expand Down
1 change: 0 additions & 1 deletion junit-platform-commons/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
org.junit.jupiter.params,
org.junit.platform.console,
org.junit.platform.engine,
org.junit.platform.jfr,
org.junit.platform.launcher,
org.junit.platform.reporting,
org.junit.platform.suite.api,
Expand Down
23 changes: 0 additions & 23 deletions junit-platform-jfr/junit-platform-jfr.gradle.kts

This file was deleted.

35 changes: 0 additions & 35 deletions junit-platform-jfr/src/main/java/module-info.java

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion junit-platform-jfr/src/test/README.md

This file was deleted.

1 change: 1 addition & 0 deletions junit-platform-launcher/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

requires static transitive org.apiguardian.api;
requires static org.jspecify;
requires static jdk.jfr;

requires transitive java.logging;
requires transitive org.junit.platform.commons;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class ClasspathAlignmentChecker {
"org.junit.platform.commons", //
"org.junit.platform.console", //
"org.junit.platform.engine", //
"org.junit.platform.jfr", //
"org.junit.platform.launcher", //
"org.junit.platform.reporting", //
"org.junit.platform.suite.api", //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.junit.platform.launcher.LauncherSessionListener;
import org.junit.platform.launcher.PostDiscoveryFilter;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.jfr.JfrUtils;

/**
* Factory for creating {@link Launcher} instances by invoking {@link #create()}
Expand Down Expand Up @@ -139,6 +140,7 @@ private static DefaultLauncher createDefaultLauncher(LauncherConfig config,
Set<TestEngine> engines = collectTestEngines(config);
List<PostDiscoveryFilter> filters = collectPostDiscoveryFilters(config);
DefaultLauncher launcher = new DefaultLauncher(engines, filters, sessionLevelStore);
JfrUtils.registerListeners(launcher);
registerLauncherDiscoveryListeners(config, launcher);
registerTestExecutionListeners(config, launcher, configurationParameters);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@
* https://www.eclipse.org/legal/epl-v20.html
*/

package org.junit.platform.jfr;
package org.junit.platform.launcher.jfr;

import static java.util.Objects.requireNonNull;
import static org.apiguardian.api.API.Status.STABLE;
import static org.apiguardian.api.API.Status.INTERNAL;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

import jdk.jfr.Category;
import jdk.jfr.Event;
Expand All @@ -40,50 +38,62 @@
* @since 1.8
* @see <a href="https://openjdk.java.net/jeps/328">JEP 328: Flight Recorder</a>
*/
@API(status = STABLE, since = "1.11")
public class FlightRecordingDiscoveryListener implements LauncherDiscoveryListener {
@API(status = INTERNAL, since = "6.0")
class FlightRecordingDiscoveryListener implements LauncherDiscoveryListener {

private final AtomicReference<@Nullable LauncherDiscoveryEvent> launcherDiscoveryEvent = new AtomicReference<>();
private final Map<org.junit.platform.engine.UniqueId, EngineDiscoveryEvent> engineDiscoveryEvents = new ConcurrentHashMap<>();
private final Map<org.junit.platform.engine.UniqueId, EngineDiscoveryEvent> engineDiscoveryEvents = new HashMap<>();
private @Nullable LauncherDiscoveryEvent launcherDiscoveryEvent;

@Override
public void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {
LauncherDiscoveryEvent event = new LauncherDiscoveryEvent();
event.selectors = request.getSelectorsByType(DiscoverySelector.class).size();
event.filters = request.getFiltersByType(DiscoveryFilter.class).size();
event.begin();
launcherDiscoveryEvent.set(event);
var event = new LauncherDiscoveryEvent();
if (event.isEnabled()) {
event.begin();
this.launcherDiscoveryEvent = event;
}
}

@Override
public void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {
requireNonNull(launcherDiscoveryEvent.getAndSet(null)).commit();
LauncherDiscoveryEvent event = this.launcherDiscoveryEvent;
this.launcherDiscoveryEvent = null;
if (event != null && event.shouldCommit()) {
event.selectors = request.getSelectorsByType(DiscoverySelector.class).size();
event.filters = request.getFiltersByType(DiscoveryFilter.class).size();
event.commit();
}
}

@Override
public void engineDiscoveryStarted(org.junit.platform.engine.UniqueId engineId) {
EngineDiscoveryEvent event = new EngineDiscoveryEvent();
event.uniqueId = engineId.toString();
event.begin();
engineDiscoveryEvents.put(engineId, event);
var event = new EngineDiscoveryEvent();
if (event.isEnabled()) {
event.begin();
this.engineDiscoveryEvents.put(engineId, event);
}
}

@Override
public void engineDiscoveryFinished(org.junit.platform.engine.UniqueId engineId, EngineDiscoveryResult result) {
EngineDiscoveryEvent event = engineDiscoveryEvents.remove(engineId);
event.result = result.getStatus().toString();
event.commit();
EngineDiscoveryEvent event = this.engineDiscoveryEvents.remove(engineId);
if (event != null && event.shouldCommit()) {
event.uniqueId = engineId.toString();
event.result = result.getStatus().toString();
event.commit();
}
}

@Override
public void issueEncountered(org.junit.platform.engine.UniqueId engineId, DiscoveryIssue issue) {
DiscoveryIssueEvent event = new DiscoveryIssueEvent();
event.engineId = engineId.toString();
event.severity = issue.severity().name();
event.message = issue.message();
event.source = issue.source().map(Object::toString).orElse(null);
event.cause = issue.cause().map(ExceptionUtils::readStackTrace).orElse(null);
event.commit();
var event = new DiscoveryIssueEvent();
if (event.shouldCommit()) {
event.engineId = engineId.toString();
event.severity = issue.severity().name();
event.message = issue.message();
event.source = issue.source().map(Object::toString).orElse(null);
event.cause = issue.cause().map(ExceptionUtils::readStackTrace).orElse(null);
event.commit();
}
}

@Category({ "JUnit", "Discovery" })
Expand Down
Loading