Skip to content

Commit e6b25a9

Browse files
WIP Enable filtering of killing test units
Enable filtering of tests in a test unit once a killing test has been discovered and the ResultCollector requested the test unit to stop execution. This might be beneficial when JUnit5TestUnit represents a test container (e.g., @RepeatedTest, @ParameterizedTest or @testfactory) that has a large number of tests or some expensive tests.
1 parent e575660 commit e6b25a9

13 files changed

+577
-135
lines changed

pom.xml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<assertj.version>3.11.1</assertj.version>
2020
<junit.platform.version>1.3.1</junit.platform.version>
2121
<junit.version>5.3.1</junit.version>
22-
<mockito.version>2.7.6</mockito.version>
22+
<mockito.version>2.23.0</mockito.version>
2323
<pitest.version>1.4.3</pitest.version>
2424
</properties>
2525

@@ -106,14 +106,27 @@
106106
<groupId>org.junit.jupiter</groupId>
107107
<artifactId>junit-jupiter-api</artifactId>
108108
<version>${junit.version}</version>
109-
<scope>test</scope>
110109
</dependency>
110+
111+
<!-- Test dependencies: -->
111112
<dependency>
112113
<groupId>org.junit.jupiter</groupId>
113114
<artifactId>junit-jupiter-params</artifactId>
114115
<version>${junit.version}</version>
115116
<scope>test</scope>
116117
</dependency>
118+
<dependency>
119+
<groupId>org.mockito</groupId>
120+
<artifactId>mockito-core</artifactId>
121+
<version>${mockito.version}</version>
122+
<scope>test</scope>
123+
</dependency>
124+
<dependency>
125+
<groupId>org.mockito</groupId>
126+
<artifactId>mockito-junit-jupiter</artifactId>
127+
<version>${mockito.version}</version>
128+
<scope>test</scope>
129+
</dependency>
117130
<dependency>
118131
<groupId>org.assertj</groupId>
119132
<artifactId>assertj-core</artifactId>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.pitest.junit5;
2+
3+
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
4+
import org.junit.jupiter.api.extension.ExecutionCondition;
5+
import org.junit.jupiter.api.extension.ExtensionContext;
6+
7+
/**
8+
* A test filter accepting all tests until requested to reject
9+
* all subsequent tests through an {@linkplain TestUnitExecutionsRegistry executions registry}.
10+
*/
11+
public class ConditionalTestFilter implements ExecutionCondition {
12+
13+
static final String EXECUTION_ID_KEY = "org.pitest.junit5.executionId";
14+
15+
private static final ConditionEvaluationResult DISABLED = ConditionEvaluationResult.disabled(
16+
"Pitest has requested to skip remaining tests in a test unit");
17+
private static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled(null);
18+
19+
private final TestUnitExecutionsRegistry executionsRegistry;
20+
21+
@SuppressWarnings("unused") // Used through reflection by the ServiceLoader
22+
// when this extension is requested by JUnit Jupiter Engine
23+
public ConditionalTestFilter() {
24+
this(TestUnitExecutionsRegistry.getInstance());
25+
}
26+
27+
// Visible for testing to be able to inject executionsRegistry
28+
ConditionalTestFilter(TestUnitExecutionsRegistry executionsRegistry) {
29+
this.executionsRegistry = executionsRegistry;
30+
}
31+
32+
@Override
33+
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
34+
boolean shallReject = context.getConfigurationParameter(EXECUTION_ID_KEY)
35+
.map(this::shallRejectTests)
36+
.orElse(false);
37+
38+
if (shallReject) {
39+
return DISABLED;
40+
} else {
41+
return ENABLED;
42+
}
43+
}
44+
45+
private boolean shallRejectTests(String executionId) {
46+
return executionsRegistry.getStatus(executionId) == TestUnitExecutionsRegistry.Status.ABORT;
47+
}
48+
}
Lines changed: 127 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,127 @@
1-
/*
2-
* Copyright 2017 Tobias Stadler
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing,
11-
* software distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and limitations under the License.
14-
*/
15-
package org.pitest.junit5;
16-
17-
import java.util.Optional;
18-
19-
import org.junit.platform.engine.TestExecutionResult;
20-
import org.junit.platform.engine.discovery.DiscoverySelectors;
21-
import org.junit.platform.engine.support.descriptor.MethodSource;
22-
import org.junit.platform.launcher.Launcher;
23-
import org.junit.platform.launcher.LauncherDiscoveryRequest;
24-
import org.junit.platform.launcher.TestExecutionListener;
25-
import org.junit.platform.launcher.TestIdentifier;
26-
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
27-
import org.junit.platform.launcher.core.LauncherFactory;
28-
import org.pitest.testapi.AbstractTestUnit;
29-
import org.pitest.testapi.Description;
30-
import org.pitest.testapi.ResultCollector;
31-
32-
/**
33-
*
34-
* @author Tobias Stadler
35-
*/
36-
public class JUnit5TestUnit extends AbstractTestUnit {
37-
38-
private final Class<?> testClass;
39-
40-
private final TestIdentifier testIdentifier;
41-
42-
public JUnit5TestUnit(Class<?> testClass, TestIdentifier testIdentifier) {
43-
super(new Description(testIdentifier.getDisplayName(), testClass));
44-
this.testClass = testClass;
45-
this.testIdentifier = testIdentifier;
46-
}
47-
48-
@Override
49-
public void execute(ResultCollector resultCollector) {
50-
Launcher launcher = LauncherFactory.create();
51-
LauncherDiscoveryRequest launcherDiscoveryRequest = LauncherDiscoveryRequestBuilder
52-
.request()
53-
.selectors(DiscoverySelectors.selectUniqueId(testIdentifier.getUniqueId()))
54-
.build();
55-
56-
launcher.registerTestExecutionListeners(new TestExecutionListener() {
57-
@Override
58-
public void executionSkipped(TestIdentifier testIdentifier, String reason) {
59-
testIdentifier.getSource().ifPresent(testSource -> {
60-
if (testSource instanceof MethodSource) {
61-
resultCollector.notifySkipped(new Description(testIdentifier.getDisplayName(), testClass));
62-
}
63-
});
64-
}
65-
66-
@Override
67-
public void executionStarted(TestIdentifier testIdentifier) {
68-
testIdentifier.getSource().ifPresent(testSource -> {
69-
if (testSource instanceof MethodSource) {
70-
resultCollector.notifyStart(new Description(testIdentifier.getDisplayName(), testClass));
71-
}
72-
});
73-
}
74-
75-
@Override
76-
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
77-
testIdentifier.getSource().ifPresent(testSource -> {
78-
if (testSource instanceof MethodSource) {
79-
Optional<Throwable> throwable = testExecutionResult.getThrowable();
80-
81-
if (TestExecutionResult.Status.ABORTED == testExecutionResult.getStatus()) {
82-
// abort treated as success
83-
// see: https://junit.org/junit5/docs/5.0.0/api/org/junit/jupiter/api/Assumptions.html
84-
resultCollector.notifyEnd(new Description(testIdentifier.getDisplayName(), testClass));
85-
} else if (throwable.isPresent()) {
86-
resultCollector.notifyEnd(new Description(testIdentifier.getDisplayName(), testClass), throwable.get());
87-
} else {
88-
resultCollector.notifyEnd(new Description(testIdentifier.getDisplayName(), testClass));
89-
}
90-
}
91-
});
92-
}
93-
94-
});
95-
launcher.execute(launcherDiscoveryRequest);
96-
}
97-
98-
}
1+
/*
2+
* Copyright 2017 Tobias Stadler
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing,
11+
* software distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and limitations under the License.
14+
*/
15+
package org.pitest.junit5;
16+
17+
import org.junit.platform.engine.TestExecutionResult;
18+
import org.junit.platform.engine.discovery.DiscoverySelectors;
19+
import org.junit.platform.engine.support.descriptor.MethodSource;
20+
import org.junit.platform.launcher.Launcher;
21+
import org.junit.platform.launcher.LauncherDiscoveryRequest;
22+
import org.junit.platform.launcher.TestExecutionListener;
23+
import org.junit.platform.launcher.TestIdentifier;
24+
import org.junit.platform.launcher.TestPlan;
25+
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
26+
import org.junit.platform.launcher.core.LauncherFactory;
27+
import org.pitest.testapi.AbstractTestUnit;
28+
import org.pitest.testapi.Description;
29+
import org.pitest.testapi.ResultCollector;
30+
31+
import java.util.Optional;
32+
33+
/**
34+
* @author Tobias Stadler
35+
*/
36+
public class JUnit5TestUnit extends AbstractTestUnit {
37+
38+
private final Class<?> testClass;
39+
40+
private final TestIdentifier testIdentifier;
41+
42+
public JUnit5TestUnit(Class<?> testClass, TestIdentifier testIdentifier) {
43+
super(new Description(testIdentifier.getDisplayName(), testClass));
44+
this.testClass = testClass;
45+
this.testIdentifier = testIdentifier;
46+
}
47+
48+
@Override
49+
public void execute(ResultCollector resultCollector) {
50+
Launcher launcher = LauncherFactory.create();
51+
52+
String executionId = getExecutionId(resultCollector);
53+
LauncherDiscoveryRequest launcherDiscoveryRequest = LauncherDiscoveryRequestBuilder
54+
.request()
55+
.configurationParameter("junit.jupiter.extensions.autodetection.enabled", "true")
56+
.configurationParameter(ConditionalTestFilter.EXECUTION_ID_KEY, executionId)
57+
.selectors(DiscoverySelectors.selectUniqueId(testIdentifier.getUniqueId()))
58+
.build();
59+
60+
// todo: shall we inject the registry?
61+
TestUnitExecutionsRegistry executionsRegistry = TestUnitExecutionsRegistry.getInstance();
62+
launcher.registerTestExecutionListeners(new TestExecutionListener() {
63+
@Override
64+
public void testPlanExecutionStarted(TestPlan testPlan) {
65+
executionsRegistry.add(executionId);
66+
}
67+
68+
@Override
69+
public void testPlanExecutionFinished(TestPlan testPlan) {
70+
executionsRegistry.remove(executionId);
71+
}
72+
73+
@Override
74+
public void executionSkipped(TestIdentifier testIdentifier, String reason) {
75+
testIdentifier.getSource().ifPresent(testSource -> {
76+
if (testSource instanceof MethodSource) {
77+
resultCollector
78+
.notifySkipped(new Description(testIdentifier.getDisplayName(), testClass));
79+
}
80+
});
81+
}
82+
83+
@Override
84+
public void executionStarted(TestIdentifier testIdentifier) {
85+
testIdentifier.getSource().ifPresent(testSource -> {
86+
if (testSource instanceof MethodSource) {
87+
resultCollector
88+
.notifyStart(new Description(testIdentifier.getDisplayName(), testClass));
89+
}
90+
});
91+
}
92+
93+
@Override
94+
public void executionFinished(TestIdentifier testIdentifier,
95+
TestExecutionResult testExecutionResult) {
96+
testIdentifier.getSource().ifPresent(testSource -> {
97+
if (testSource instanceof MethodSource) {
98+
Optional<Throwable> throwable = testExecutionResult.getThrowable();
99+
100+
if (TestExecutionResult.Status.ABORTED == testExecutionResult.getStatus()) {
101+
// abort treated as success
102+
// see: https://junit.org/junit5/docs/5.0.0/api/org/junit/jupiter/api/Assumptions.html
103+
resultCollector.notifyEnd(new Description(testIdentifier.getDisplayName(), testClass));
104+
} else if (throwable.isPresent()) {
105+
resultCollector.notifyEnd(new Description(testIdentifier.getDisplayName(), testClass),
106+
throwable.get());
107+
108+
if (resultCollector.shouldExit()) {
109+
executionsRegistry.abortExecution(executionId);
110+
}
111+
} else {
112+
resultCollector
113+
.notifyEnd(new Description(testIdentifier.getDisplayName(), testClass));
114+
}
115+
}
116+
});
117+
}
118+
119+
});
120+
launcher.execute(launcherDiscoveryRequest);
121+
}
122+
123+
private String getExecutionId(ResultCollector resultCollector) {
124+
return testIdentifier.getUniqueId() + ":"
125+
+ System.identityHashCode(resultCollector);
126+
}
127+
}

src/main/java/org/pitest/junit5/JUnit5TestUnitFinder.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@
1414
*/
1515
package org.pitest.junit5;
1616

17-
import java.util.List;
18-
import java.util.Set;
19-
20-
import static java.util.Collections.emptyList;
21-
import static java.util.stream.Collectors.toList;
2217
import org.junit.platform.engine.discovery.DiscoverySelectors;
2318
import org.junit.platform.engine.support.descriptor.MethodSource;
2419
import org.junit.platform.launcher.Launcher;
@@ -28,6 +23,12 @@
2823
import org.pitest.testapi.TestUnit;
2924
import org.pitest.testapi.TestUnitFinder;
3025

26+
import java.util.List;
27+
import java.util.Set;
28+
29+
import static java.util.Collections.emptyList;
30+
import static java.util.stream.Collectors.toList;
31+
3132
/**
3233
*
3334
* @author Tobias Stadler

0 commit comments

Comments
 (0)