Skip to content

Commit f6f1a70

Browse files
authored
Introduce LauncherExecutionRequest (#4724)
The new `Launcher.execute(LauncherExecutionRequest)` method paves the road for adding additional parameters to test execution without having to add additional overloads of `execute()`. For example, for #1880 we will likely need to introduce a `CancellationToken` (as drafted in #4709). Instead of having to add two new overloads, such changes will then be easy because only `LauncherExecutionRequest` will need to be changed. `LauncherExecutionRequestBuilder` provides a fluent API for constructing execution requests starting either with a `TestPlan` or a `LauncherDiscoveryRequest`. In addition, `LauncherDiscoveryRequestBuilder.forExecution()` is a convenience method to create an execution request from a discovery request.
1 parent da4b045 commit f6f1a70

32 files changed

+518
-116
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-6.0.0-M2.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,19 @@ repository on GitHub.
2323

2424
* Discontinue `junit-platform-suite-commons` which is now integrated into
2525
`junit-platform-suite`.
26+
* Deprecate `Launcher.execute(TestPlan, TestExecutionListener[])` and
27+
`Launcher.execute(LauncherDiscoveryRequest, TestExecutionListener[])` in favor of
28+
`Launcher.execute(LauncherExecutionRequest)`
2629

2730
[[release-notes-6.0.0-M2-junit-platform-new-features-and-improvements]]
2831
==== New Features and Improvements
2932

33+
* Introduce new `Launcher.execute(LauncherExecutionRequest)` API with corresponding
34+
`LauncherExecutionRequestBuilder` to enable the addition of parameters to test
35+
executions without additional overloads of `execute`.
36+
* Introduce `LauncherDiscoveryRequestBuilder.forExecution()` method as a convenience
37+
method for constructing a `LauncherExecutionRequest` that contains a
38+
`LauncherDiscoveryRequest`.
3039
* Introduce `TestTask.getTestDescriptor()` method for use in
3140
`HierarchicalTestExecutorService` implementations.
3241

documentation/src/test/java/example/UsingTheLauncherDemo.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
import org.junit.platform.launcher.Launcher;
2424
import org.junit.platform.launcher.LauncherDiscoveryListener;
2525
import org.junit.platform.launcher.LauncherDiscoveryRequest;
26+
import org.junit.platform.launcher.LauncherExecutionRequest;
2627
import org.junit.platform.launcher.LauncherSession;
2728
import org.junit.platform.launcher.LauncherSessionListener;
2829
import org.junit.platform.launcher.PostDiscoveryFilter;
2930
import org.junit.platform.launcher.TestExecutionListener;
3031
import org.junit.platform.launcher.TestPlan;
3132
import org.junit.platform.launcher.core.LauncherConfig;
3233
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
34+
import org.junit.platform.launcher.core.LauncherExecutionRequestBuilder;
3335
import org.junit.platform.launcher.core.LauncherFactory;
3436
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
3537
import org.junit.platform.launcher.listeners.TestExecutionSummary;
@@ -74,7 +76,7 @@ void discovery() {
7476
void execution() {
7577
// @formatter:off
7678
// tag::execution[]
77-
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
79+
LauncherDiscoveryRequest discoveryRequest = LauncherDiscoveryRequestBuilder.request()
7880
.selectors(
7981
selectPackage("com.example.mytests"),
8082
selectClass(MyTestClass.class)
@@ -94,11 +96,11 @@ void execution() {
9496
// Register a listener of your choice
9597
launcher.registerTestExecutionListeners(listener);
9698
// Discover tests and build a test plan
97-
TestPlan testPlan = launcher.discover(request);
99+
TestPlan testPlan = launcher.discover(discoveryRequest);
98100
// Execute test plan
99-
launcher.execute(testPlan);
100-
// Alternatively, execute the request directly
101-
launcher.execute(request);
101+
launcher.execute(LauncherExecutionRequestBuilder.request(testPlan).build());
102+
// Alternatively, execute the discoveryRequest request directly
103+
launcher.execute(LauncherExecutionRequestBuilder.request(discoveryRequest).build());
102104
}
103105

104106
TestExecutionSummary summary = listener.getSummary();
@@ -128,8 +130,9 @@ void launcherConfig() {
128130
.addTestExecutionListeners(new CustomTestExecutionListener())
129131
.build();
130132

131-
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
133+
LauncherExecutionRequest request = LauncherDiscoveryRequestBuilder.request()
132134
.selectors(selectPackage("com.example.mytests"))
135+
.forExecution()
133136
.build();
134137

135138
try (LauncherSession session = LauncherFactory.openSession(launcherConfig)) {

documentation/src/test/java/example/sharedresources/SharedResourceDemo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ void runBothCustomEnginesTest() {
3939
// tag::custom_line_break[]
4040
.build());
4141

42-
launcher.execute(request().build());
42+
launcher.execute(request().forExecution().build());
4343

4444
assertSame(firstCustomEngine.socket, secondCustomEngine.socket);
4545
assertTrue(firstCustomEngine.socket.isClosed(), "socket should be closed");

junit-platform-console/src/main/java/org/junit/platform/console/tasks/ConsoleTestExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ private void launchTests(Launcher launcher, Optional<Path> reportsDir) {
143143
LauncherDiscoveryRequestBuilder discoveryRequestBuilder = toDiscoveryRequestBuilder(discoveryOptions);
144144
reportsDir.ifPresent(dir -> discoveryRequestBuilder.configurationParameter(OUTPUT_DIR_PROPERTY_NAME,
145145
dir.toAbsolutePath().toString()));
146-
launcher.execute(discoveryRequestBuilder.build());
146+
launcher.execute(discoveryRequestBuilder.forExecution().build());
147147
}
148148

149149
private Optional<ClassLoader> createCustomClassLoader() {

junit-platform-launcher/src/main/java/org/junit/platform/launcher/Launcher.java

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010

1111
package org.junit.platform.launcher;
1212

13+
import static org.apiguardian.api.API.Status.DEPRECATED;
14+
import static org.apiguardian.api.API.Status.MAINTAINED;
1315
import static org.apiguardian.api.API.Status.STABLE;
1416

1517
import org.apiguardian.api.API;
18+
import org.junit.platform.launcher.core.LauncherExecutionRequestBuilder;
1619

1720
/**
1821
* The {@code Launcher} API is the main entry point for client code that
@@ -105,8 +108,16 @@ public interface Launcher {
105108
*
106109
* @param launcherDiscoveryRequest the launcher discovery request; never {@code null}
107110
* @param listeners additional test execution listeners; never {@code null}
111+
* @deprecated Please use {@link #execute(LauncherExecutionRequest)} instead.
108112
*/
109-
void execute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecutionListener... listeners);
113+
@Deprecated
114+
@API(status = DEPRECATED, since = "6.0")
115+
default void execute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecutionListener... listeners) {
116+
var executionRequest = LauncherExecutionRequestBuilder.request(launcherDiscoveryRequest) //
117+
.listeners(listeners) //
118+
.build();
119+
execute(executionRequest);
120+
}
110121

111122
/**
112123
* Execute the supplied {@link TestPlan} and notify
@@ -123,8 +134,44 @@ public interface Launcher {
123134
* @param testPlan the test plan to execute; never {@code null}
124135
* @param listeners additional test execution listeners; never {@code null}
125136
* @since 1.4
137+
* @deprecated Please use {@link #execute(LauncherExecutionRequest)} instead.
126138
*/
127-
@API(status = STABLE, since = "1.4")
128-
void execute(TestPlan testPlan, TestExecutionListener... listeners);
139+
@Deprecated
140+
@API(status = DEPRECATED, since = "6.0")
141+
default void execute(TestPlan testPlan, TestExecutionListener... listeners) {
142+
var executionRequest = LauncherExecutionRequestBuilder.request(testPlan) //
143+
.listeners(listeners) //
144+
.build();
145+
execute(executionRequest);
146+
}
147+
148+
/**
149+
* Execute tests according to the supplied {@link LauncherExecutionRequest}
150+
* {@linkplain #registerTestExecutionListeners registered listeners} about
151+
* the progress and results of the execution.
152+
*
153+
* <p>Test execution listeners supplied
154+
* {@linkplain LauncherExecutionRequest#getAdditionalTestExecutionListeners()
155+
* as part of the request} are registered in addition to already registered
156+
* listeners but only for the supplied execution request.
157+
*
158+
* @apiNote If the execution request contains a {@link TestPlan} rather than
159+
* a {@link LauncherDiscoveryRequest}, it must not have been executed
160+
* previously.
161+
*
162+
* <p>If the execution request contains a {@link LauncherDiscoveryRequest},
163+
* calling this method will cause test discovery to be executed for all
164+
* registered engines. If the same {@link LauncherDiscoveryRequest} was
165+
* previously passed to {@link #discover(LauncherDiscoveryRequest)}, you
166+
* should instead provide the resulting {@link TestPlan} as part of the
167+
* supplied execution request to avoid the potential performance degradation
168+
* (e.g., classpath scanning) of running test discovery twice.
169+
*
170+
* @param launcherExecutionRequest the launcher execution request; never
171+
* {@code null}
172+
* @since 6.0
173+
*/
174+
@API(status = MAINTAINED, since = "6.0")
175+
void execute(LauncherExecutionRequest launcherExecutionRequest);
129176

130177
}

junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,7 @@ public class LauncherConstants {
257257
* <p>If not specified, the {@code Launcher} will report discovery issues
258258
* during the discovery phase if
259259
* {@link Launcher#discover(LauncherDiscoveryRequest)} is called, and during
260-
* the execution phase if
261-
* {@link Launcher#execute(LauncherDiscoveryRequest, TestExecutionListener...)}
260+
* the execution phase if {@link Launcher#execute(LauncherExecutionRequest)}
262261
* is called.
263262
*
264263
* @since 1.13

junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryRequest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
* in the test plan.</li>
4444
* </ul>
4545
*
46+
* <p>This interface is not intended to be implemented by clients.
47+
*
4648
* @since 1.0
4749
* @see org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder
4850
* @see EngineDiscoveryRequest
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2015-2025 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.platform.launcher;
12+
13+
import static org.apiguardian.api.API.Status.MAINTAINED;
14+
15+
import java.util.Collection;
16+
import java.util.Optional;
17+
18+
import org.apiguardian.api.API;
19+
20+
/**
21+
* {@code LauncherExecutionRequest} encapsulates a request for test execution
22+
* passed to the {@link Launcher}.
23+
*
24+
* <p>Most importantly, a {@code LauncherExecutionRequest} contains either a
25+
* {@link LauncherDiscoveryRequest} for on-the-fly test discovery or a
26+
* {@link TestPlan} that has previously been discovered.
27+
*
28+
* <p>Moreover, a {@code LauncherExecutionRequest} may contain the following:
29+
*
30+
* <ul>
31+
* <li>Additional {@linkplain TestExecutionListener Test Execution Listeners}
32+
* that should be notified of events pertaining to this execution request.</li>
33+
* </ul>
34+
*
35+
* <p>This interface is not intended to be implemented by clients.
36+
*
37+
* @since 6.0
38+
* @see org.junit.platform.launcher.core.LauncherExecutionRequestBuilder
39+
* @see Launcher#execute(LauncherExecutionRequest)
40+
*/
41+
@API(status = MAINTAINED, since = "6.0")
42+
public interface LauncherExecutionRequest {
43+
44+
/**
45+
* {@return the test plan for this execution request}
46+
*
47+
* <p>If absent, a {@link TestPlan} will be present.
48+
*/
49+
Optional<TestPlan> getTestPlan();
50+
51+
/**
52+
* {@return the discovery request for this execution request}
53+
*
54+
* <p>If absent, a {@link TestPlan} will be present.
55+
*/
56+
Optional<LauncherDiscoveryRequest> getDiscoveryRequest();
57+
58+
/**
59+
* {@return the collection of additional test execution listeners that
60+
* should be notified about events pertaining to this execution request}
61+
*/
62+
Collection<? extends TestExecutionListener> getAdditionalTestExecutionListeners();
63+
64+
}

junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherInterceptor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
* </li>
3333
* <li>
3434
* calls to {@link Launcher#discover(LauncherDiscoveryRequest)},
35-
* {@link Launcher#execute(TestPlan, TestExecutionListener...)}, and
36-
* {@link Launcher#execute(LauncherDiscoveryRequest, TestExecutionListener...)}
35+
* {@link Launcher#execute(TestPlan, TestExecutionListener...)},
36+
* {@link Launcher#execute(LauncherDiscoveryRequest, TestExecutionListener...)},
37+
* and {@link Launcher#execute(LauncherExecutionRequest)},
3738
* </li>
3839
* </ul>
3940
*

junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestExecutionListener.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222

2323
/**
2424
* Register a concrete implementation of this interface with a {@link Launcher}
25-
* to be notified of events that occur during test execution.
25+
* or {@link LauncherExecutionRequest} to be notified of events that occur
26+
* during test execution.
2627
*
2728
* <p>All methods in this interface have empty <em>default</em> implementations.
2829
* Concrete implementations may therefore override one or more of these methods

0 commit comments

Comments
 (0)