1717import java .util .ArrayList ;
1818import java .util .Collection ;
1919import java .util .List ;
20+ import java .util .Map ;
21+ import java .util .concurrent .ConcurrentHashMap ;
22+ import java .util .concurrent .locks .ReentrantLock ;
2023
2124import static java .util .Collections .emptyList ;
2225import static java .util .Collections .synchronizedList ;
2629import org .junit .platform .commons .PreconditionViolationException ;
2730import org .junit .platform .engine .Filter ;
2831import org .junit .platform .engine .TestExecutionResult ;
32+ import org .junit .platform .engine .UniqueId ;
2933import org .junit .platform .engine .discovery .DiscoverySelectors ;
3034import org .junit .platform .engine .support .descriptor .MethodSource ;
3135import org .junit .platform .launcher .Launcher ;
3539import org .junit .platform .launcher .core .LauncherDiscoveryRequestBuilder ;
3640import org .junit .platform .launcher .core .LauncherFactory ;
3741import org .pitest .testapi .Description ;
42+ import org .pitest .testapi .NullExecutionListener ;
3843import org .pitest .testapi .TestGroupConfig ;
3944import org .pitest .testapi .TestUnit ;
4045import 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