Skip to content

Commit

Permalink
Merge pull request #1200 from Vampire/make-coverage-test-execution-li…
Browse files Browse the repository at this point in the history
…stener-thread-safe

Make the coverage test execution listener thread-safe
  • Loading branch information
hcoles authored May 15, 2023
2 parents 49b2327 + 4494339 commit eef9072
Showing 1 changed file with 32 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Logger;

import static java.util.concurrent.TimeUnit.NANOSECONDS;
Expand All @@ -17,8 +20,9 @@ public class CoverageTestExecutionListener implements TestUnitExecutionListener
private final ThreadMXBean threads = ManagementFactory.getThreadMXBean();

private final CoverageReceiver invokeQueue;
private long t0;
private int threadsBeforeTest;
private final Map<Description, Long> startTimes = new ConcurrentHashMap<>();
private final Map<Description, Integer> threadsBeforeTest = new ConcurrentHashMap<>();
private final AtomicLong firstThreadId = new AtomicLong();

public CoverageTestExecutionListener(CoverageReceiver invokeQueue) {
this.invokeQueue = invokeQueue;
Expand All @@ -27,26 +31,42 @@ public CoverageTestExecutionListener(CoverageReceiver invokeQueue) {
@Override
public void executionStarted(Description description) {
LOG.fine(() -> "Gathering coverage for test " + description);
t0 = System.nanoTime();
threadsBeforeTest = this.threads.getThreadCount();
startTimes.put(description, System.nanoTime());
if (!firstThreadId.compareAndSet(0, Thread.currentThread().getId())
&& (firstThreadId.get() != Thread.currentThread().getId())
&& (threadsBeforeTest.size() > 0)) {
LOG.warning("Tests are run in parallel. Coverage recording most likely will not work properly.");
}
threadsBeforeTest.put(description, threads.getThreadCount());
}

@Override
public void executionFinished(Description description, boolean passed) {
int executionTime = (int) NANOSECONDS.toMillis(System.nanoTime() - t0);
if (executionTime < 0) {
LOG.warning("Recorded negative test time. Test life cycle not as expected.");
// substitute an unimportant, but high, time for this test so it is unlikely to
Long t0 = startTimes.remove(description);
int executionTime;
if (t0 == null) {
LOG.warning("Recorded no start time. Test life cycle not as expected.");
// substitute an unimportant, but high, time for this test, so it is unlikely to
// be prioritised above others.
executionTime = 120000;
executionTime = 120_000;
} else {
executionTime = (int) NANOSECONDS.toMillis(System.nanoTime() - t0);
if (executionTime < 0) {
LOG.warning("Recorded negative test time. Test life cycle not as expected.");
// substitute an unimportant, but high, time for this test, so it is unlikely to
// be prioritised above others.
executionTime = 120_000;
}
}

final int threadsAfterTest = this.threads.getThreadCount();
if (threadsAfterTest > threadsBeforeTest) {
final int threadsAfterTest = threads.getThreadCount();
if (threadsAfterTest > threadsBeforeTest.getOrDefault(description, 0)) {
LOG.warning("More threads at end of test (" + threadsAfterTest + ") "
+ description + " than start. ("
+ threadsBeforeTest + ")");
}
this.invokeQueue.recordTestOutcome(description, passed, executionTime);
threadsBeforeTest.remove(description);

invokeQueue.recordTestOutcome(description, passed, executionTime);
}
}

0 comments on commit eef9072

Please sign in to comment.