Skip to content

Commit 963fd1e

Browse files
committed
Serialize tests during coverage calculation
This generically fixes hcoles/pitest#760 and #73 for all platform engines, removing the Jupiter specific work-around from #74 and serializing test execution during coverage calculation using locks. This way the tests can also properly run in parallel later on during mutant hunting.
1 parent 73391eb commit 963fd1e

2 files changed

Lines changed: 35 additions & 4 deletions

File tree

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,10 @@
2727
public class JUnit5TestPluginFactory implements TestPluginFactory {
2828

2929
@Override
30-
public Configuration createTestFrameworkConfiguration(TestGroupConfig config,
31-
ClassByteArraySource source,
30+
public Configuration createTestFrameworkConfiguration(TestGroupConfig config,
31+
ClassByteArraySource source,
3232
Collection<String> excludedRunners,
3333
Collection<String> includedTestMethods) {
34-
System.setProperty("junit.jupiter.execution.parallel.enabled", "false");
3534
return new JUnit5Configuration(config, includedTestMethods);
3635
}
3736

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import java.util.ArrayList;
1818
import java.util.Collection;
1919
import java.util.List;
20+
import java.util.Map;
21+
import java.util.concurrent.ConcurrentHashMap;
22+
import java.util.concurrent.locks.ReentrantLock;
2023

2124
import static java.util.Collections.emptyList;
2225
import static java.util.Collections.synchronizedList;
@@ -26,6 +29,7 @@
2629
import org.junit.platform.commons.PreconditionViolationException;
2730
import org.junit.platform.engine.Filter;
2831
import org.junit.platform.engine.TestExecutionResult;
32+
import org.junit.platform.engine.UniqueId;
2933
import org.junit.platform.engine.discovery.DiscoverySelectors;
3034
import org.junit.platform.engine.support.descriptor.MethodSource;
3135
import org.junit.platform.launcher.Launcher;
@@ -35,6 +39,7 @@
3539
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
3640
import org.junit.platform.launcher.core.LauncherFactory;
3741
import org.pitest.testapi.Description;
42+
import org.pitest.testapi.NullExecutionListener;
3843
import org.pitest.testapi.TestGroupConfig;
3944
import org.pitest.testapi.TestUnit;
4045
import org.pitest.testapi.TestUnitExecutionListener;
@@ -97,10 +102,15 @@ private class TestIdentifierListener implements TestExecutionListener {
97102
private final Class<?> testClass;
98103
private final TestUnitExecutionListener l;
99104
private final List<TestIdentifier> identifiers = synchronizedList(new ArrayList<>());
105+
private final boolean serializeExecution;
106+
private final Map<UniqueId, ReentrantLock> parentCoverageSerializers = new ConcurrentHashMap<>();
107+
private final Map<UniqueId, ReentrantLock> coverageSerializers = new ConcurrentHashMap<>();
108+
private final ReentrantLock rootCoverageSerializer = new ReentrantLock();
100109

101110
public TestIdentifierListener(Class<?> testClass, TestUnitExecutionListener l) {
102111
this.testClass = testClass;
103112
this.l = l;
113+
serializeExecution = !(l instanceof NullExecutionListener);
104114
}
105115

106116
List<TestIdentifier> getIdentifiers() {
@@ -117,12 +127,26 @@ public void executionStarted(TestIdentifier testIdentifier) {
117127
&& !includedTestMethods.contains(((MethodSource)testIdentifier.getSource().get()).getMethodName())) {
118128
return;
119129
}
130+
131+
if (serializeExecution) {
132+
coverageSerializers.compute(testIdentifier.getUniqueIdObject(), (uniqueId, lock) -> {
133+
if (lock != null) {
134+
throw new AssertionError("No lock should be present");
135+
}
136+
137+
return testIdentifier
138+
.getParentIdObject()
139+
.map(parentCoverageSerializers::get)
140+
.orElse(rootCoverageSerializer);
141+
}).lock();
142+
parentCoverageSerializers.put(testIdentifier.getUniqueIdObject(), new ReentrantLock());
143+
}
144+
120145
l.executionStarted(new Description(testIdentifier.getUniqueId(), testClass));
121146
identifiers.add(testIdentifier);
122147
}
123148
}
124149

125-
126150
@Override
127151
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
128152
// Classes with failing BeforeAlls never start execution and identify as 'containers' not 'tests'
@@ -134,6 +158,14 @@ public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult
134158
} else if (testIdentifier.isTest()) {
135159
l.executionFinished(new Description(testIdentifier.getUniqueId(), testClass), true);
136160
}
161+
162+
if (serializeExecution) {
163+
parentCoverageSerializers.remove(testIdentifier.getUniqueIdObject());
164+
ReentrantLock lock = coverageSerializers.remove(testIdentifier.getUniqueIdObject());
165+
if (lock != null) {
166+
lock.unlock();
167+
}
168+
}
137169
}
138170

139171
}

0 commit comments

Comments
 (0)