Skip to content

Commit

Permalink
Serialize tests during coverage calculation
Browse files Browse the repository at this point in the history
This generically fixes hcoles/pitest#760 and pitest#73 for all platform engines,
removing the Jupiter specific work-around from pitest#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.
  • Loading branch information
Vampire committed May 15, 2023
1 parent 0f30f27 commit f960b26
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
5 changes: 2 additions & 3 deletions src/main/java/org/pitest/junit5/JUnit5TestPluginFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@
public class JUnit5TestPluginFactory implements TestPluginFactory {

@Override
public Configuration createTestFrameworkConfiguration(TestGroupConfig config,
ClassByteArraySource source,
public Configuration createTestFrameworkConfiguration(TestGroupConfig config,
ClassByteArraySource source,
Collection<String> excludedRunners,
Collection<String> includedTestMethods) {
System.setProperty("junit.jupiter.execution.parallel.enabled", "false");
return new JUnit5Configuration(config, includedTestMethods);
}

Expand Down
34 changes: 33 additions & 1 deletion src/main/java/org/pitest/junit5/JUnit5TestUnitFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import static java.util.Collections.emptyList;
import static java.util.Collections.synchronizedList;
Expand All @@ -26,6 +29,7 @@
import org.junit.platform.commons.PreconditionViolationException;
import org.junit.platform.engine.Filter;
import org.junit.platform.engine.TestExecutionResult;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.engine.support.descriptor.MethodSource;
import org.junit.platform.launcher.Launcher;
Expand All @@ -35,6 +39,7 @@
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.pitest.testapi.Description;
import org.pitest.testapi.NullExecutionListener;
import org.pitest.testapi.TestGroupConfig;
import org.pitest.testapi.TestUnit;
import org.pitest.testapi.TestUnitExecutionListener;
Expand Down Expand Up @@ -97,10 +102,15 @@ private class TestIdentifierListener implements TestExecutionListener {
private final Class<?> testClass;
private final TestUnitExecutionListener l;
private final List<TestIdentifier> identifiers = synchronizedList(new ArrayList<>());
private final boolean serializeExecution;
private final Map<UniqueId, ReentrantLock> parentCoverageSerializers = new ConcurrentHashMap<>();
private final Map<UniqueId, ReentrantLock> coverageSerializers = new ConcurrentHashMap<>();
private final ReentrantLock rootCoverageSerializer = new ReentrantLock();

public TestIdentifierListener(Class<?> testClass, TestUnitExecutionListener l) {
this.testClass = testClass;
this.l = l;
serializeExecution = !(l instanceof NullExecutionListener);
}

List<TestIdentifier> getIdentifiers() {
Expand All @@ -117,12 +127,26 @@ public void executionStarted(TestIdentifier testIdentifier) {
&& !includedTestMethods.contains(((MethodSource)testIdentifier.getSource().get()).getMethodName())) {
return;
}

if (serializeExecution) {
coverageSerializers.compute(testIdentifier.getUniqueIdObject(), (uniqueId, lock) -> {
if (lock != null) {
throw new AssertionError("No lock should be present");
}

return testIdentifier
.getParentIdObject()
.map(parentCoverageSerializers::get)
.orElse(rootCoverageSerializer);
}).lock();
parentCoverageSerializers.put(testIdentifier.getUniqueIdObject(), new ReentrantLock());
}

l.executionStarted(new Description(testIdentifier.getUniqueId(), testClass));
identifiers.add(testIdentifier);
}
}


@Override
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
// Classes with failing BeforeAlls never start execution and identify as 'containers' not 'tests'
Expand All @@ -134,6 +158,14 @@ public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult
} else if (testIdentifier.isTest()) {
l.executionFinished(new Description(testIdentifier.getUniqueId(), testClass), true);
}

if (serializeExecution) {
parentCoverageSerializers.remove(testIdentifier.getUniqueIdObject());
ReentrantLock lock = coverageSerializers.remove(testIdentifier.getUniqueIdObject());
if (lock != null) {
lock.unlock();
}
}
}

}
Expand Down

0 comments on commit f960b26

Please sign in to comment.