From 864e68845d5b9788e402caf1c2140bfd9fd685b4 Mon Sep 17 00:00:00 2001 From: Carlos Macasaet Date: Mon, 26 Oct 2020 22:23:31 -0700 Subject: [PATCH] Fix concurrency unit tests on some platforms This commit _attempts_ to fix concurrency unit test failures observed on Windows Server 2019 10.0 amd64 with JVM 15.0.1 (Azul Systems, Inc. 15.0.1+8) and Linux 5.4.0-1031-azure amd64 with JVM 15 (Oracle Corporation 15+36-1562). It does this by using a `CyclicBarrier` to ensure test methods run concurrently when expected to. This is the same logic used by `ParallelExecutionIntegrationTests`. Note, I was not able to reproduce the build failures locally. Issue: #2229 --- .../VintageTestEngineParallelismTests.java | 77 ++++++++++++++----- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineParallelismTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineParallelismTests.java index 2946dcf98ab0..621add16d0e2 100644 --- a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineParallelismTests.java +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineParallelismTests.java @@ -14,6 +14,10 @@ import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; import java.util.IntSummaryStatistics; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.LongAdder; import org.junit.jupiter.api.Test; @@ -55,7 +59,7 @@ void verifyTestsRunConcurrently() { TestEngine engine = new VintageTestEngine(); TestDescriptor descriptor = engine.discover(discoveryRequest, UniqueId.forEngine(engine.getId())); - CountingListener listener = new CountingListener(); + CountingListener listener = new CountingListener(2); ExecutionRequest executionRequest = new ExecutionRequest(descriptor, listener, discoveryRequest.getConfigurationParameters()); @@ -79,7 +83,7 @@ void verifyTestsRunSequentially() { TestEngine engine = new VintageTestEngine(); TestDescriptor descriptor = engine.discover(discoveryRequest, UniqueId.forEngine(engine.getId())); - CountingListener listener = new CountingListener(); + CountingListener listener = new CountingListener(1); ExecutionRequest executionRequest = new ExecutionRequest(descriptor, listener, discoveryRequest.getConfigurationParameters()); @@ -102,6 +106,14 @@ protected class CountingListener implements EngineExecutionListener { private final IntSummaryStatistics testSummary = new IntSummaryStatistics(); private final LongAdder testAdder = new LongAdder(); private final LongAdder classAdder = new LongAdder(); + private final CyclicBarrier barrier; + + /** + * @param methodConcurrency the number of test methods that may execute at the same time + */ + public CountingListener(int methodConcurrency) { + this.barrier = new CyclicBarrier(methodConcurrency); + } /** * @return the largest number of tests that ran at the same time. @@ -118,31 +130,58 @@ public int getMaxConcurrentClasses() { } public void executionStarted(final TestDescriptor testDescriptor) { - synchronized (this) { - String displayName = testDescriptor.getDisplayName(); - if (testDescriptor.isTest()) { - testAdder.increment(); - testSummary.accept(testAdder.intValue()); - } - else if (displayName.equals("A") || displayName.equals("B")) { - classAdder.increment(); - classSummary.accept(classAdder.intValue()); - } + String displayName = testDescriptor.getDisplayName(); + if (testDescriptor.isTest()) { + incrementTests(); + } + else if (displayName.equals("A") || displayName.equals("B")) { + incrementClasses(); } } public void executionFinished(final TestDescriptor testDescriptor, final TestExecutionResult testExecutionResult) { - synchronized (this) { - String displayName = testDescriptor.getDisplayName(); - assertEquals(Status.SUCCESSFUL, testExecutionResult.getStatus()); - if (testDescriptor.isTest()) { + String displayName = testDescriptor.getDisplayName(); + assertEquals(Status.SUCCESSFUL, testExecutionResult.getStatus()); + if (testDescriptor.isTest()) { + decrementTests(); + } + else if (displayName.equals("A") || displayName.equals("B")) { + decrementClasses(); + } + } + + protected void incrementTests() { + synchronized (testSummary) { + testAdder.increment(); + testSummary.accept(testAdder.intValue()); + } + } + + protected void decrementTests() { + try { + barrier.await(100, TimeUnit.MILLISECONDS); + synchronized (testSummary) { testAdder.decrement(); } - else if (displayName.equals("A") || displayName.equals("B")) { - classAdder.decrement(); - } + } + catch (InterruptedException | BrokenBarrierException | TimeoutException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + protected void incrementClasses() { + synchronized (classSummary) { + classAdder.increment(); + classSummary.accept(classAdder.intValue()); + } + } + + protected void decrementClasses() { + synchronized (classSummary) { + classAdder.decrement(); } } } + }