-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Draft support for cancelling test execution #4709
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
Conversation
* @since 6.0 | ||
*/ | ||
@API(status = EXPERIMENTAL, since = "6.0") | ||
void execute(TestPlan testPlan, CancellationToken cancellationToken, TestExecutionListener... listeners); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New API for build tools and IDEs
@@ -171,4 +174,8 @@ public NamespacedHierarchicalStore<Namespace> getStore() { | |||
"No NamespacedHierarchicalStore was configured for this request"); | |||
} | |||
|
|||
public CancellationToken getCancellationToken() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New API for TestEngine
implementors
private SkipResult checkWhetherSkipped() throws Exception { | ||
return taskContext.cancellationToken().isCancellationRequested() // | ||
? CANCELLED_SKIP_RESULT // | ||
: node.shouldBeSkipped(requiredContext()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Support for engines extending HierarchicalTestEngine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Cucumber this means that cancellation will work out of the box. 👍
if (cancellationToken.isCancellationRequested()) { | ||
listener.executionSkipped(engineDescriptor, "Cancellation of execution requested"); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skip entire engine if cancellation has already been requested. This avoid the overhead of initializing an engine's execution only to skip its root node. Moreover, this is at least a stop-gap solution for engines not supporting cancellation at all.
results.allEvents().assertEventsMatchExactly( // | ||
event(engine(), started()), // | ||
event(container(testClass), started()), // | ||
event(test("first"), started()), // | ||
event(test("first"), reportEntry(Map.of("cancelled", "true"))), // | ||
event(test("first"), finishedSuccessfully()), // | ||
event(test("second"), skippedWithReason("Test execution cancelled")), // | ||
event(container(testClass), finishedSuccessfully()), // | ||
event(engine(), finishedSuccessfully())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test demonstrates what happens if execution is cancelled during execution of the first test. The second test is reported as skipped and all later test classes would be as well.
* Introduce `CancellationToken` interface and default implementation * Add `Launcher.execute(TestPlan, CancellationToken, ...)` method * Add `ExecutionRequest.getCancellationToken()` method * Implement support for cancellation for engines extending `HierarchicalTestEngine` Eventually resolves #1880.
8ec7807
to
1172d71
Compare
8eb0df7
to
45ad996
Compare
45ad996
to
617b66e
Compare
@@ -171,4 +174,8 @@ public NamespacedHierarchicalStore<Namespace> getStore() { | |||
"No NamespacedHierarchicalStore was configured for this request"); | |||
} | |||
|
|||
public CancellationToken getCancellationToken() { | |||
return Preconditions.notNull(this.cancellationToken, "No CancellationToken was configured for this request"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had some difficulty inferring from what code how I could safely use the cancellation token. Instead of making the cancellationToken
nullable, I would expect a default implementation to be provided in the deprecated constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can instead return a default one here since it has a sensible default.
@@ -73,7 +73,7 @@ public void execute(ExecutionRequest request) { | |||
suiteEngineDescriptor.getChildren() | |||
.stream() | |||
.map(SuiteTestDescriptor.class::cast) | |||
.forEach(suiteTestDescriptor -> suiteTestDescriptor.execute(engineExecutionListener, requestLevelStore)); | |||
.forEach(suiteTestDescriptor -> suiteTestDescriptor.execute(engineExecutionListener, requestLevelStore, request.getCancellationToken())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will fail if a request was created with the deprecated constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ExecutionRequest
really should be created by anyone outside of the Platform.
Since many tools; for example, Maven Surefire; do not report events for engine-level descriptors, reporting their children as skipped increases compatibility with such tools.
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.
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.
Overview
CancellationToken
interface and default implementationLauncher.execute(TestPlan, CancellationToken, ...)
methodExecutionRequest.getCancellationToken()
methodHierarchicalTestEngine
Eventually resolves #1880.
I hereby agree to the terms of the JUnit Contributor License Agreement.
Definition of Done
@API
annotations