From f9ed44040f7bf612776e8c5eb0486e1c4f4cbab0 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Sun, 17 Nov 2024 00:41:23 +0900 Subject: [PATCH] support parallelization in junit-vintage Signed-off-by: yongjunhong --- .../vintage/engine/VintageTestEngine.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java index be13d89580a7..b48042402cdc 100644 --- a/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java +++ b/junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java @@ -14,10 +14,20 @@ import static org.junit.platform.engine.TestExecutionResult.successful; import static org.junit.vintage.engine.descriptor.VintageTestDescriptor.ENGINE_ID; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import org.apiguardian.api.API; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.engine.EngineDiscoveryRequest; import org.junit.platform.engine.EngineExecutionListener; import org.junit.platform.engine.ExecutionRequest; @@ -37,6 +47,11 @@ @API(status = INTERNAL, since = "4.12") public final class VintageTestEngine implements TestEngine { + private static final Logger logger = LoggerFactory.getLogger(VintageTestEngine.class); + + private static final int DEFAULT_THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors(); + private static final int SHUTDOWN_TIMEOUT_SECONDS = 30; + @Override public String getId() { return ENGINE_ID; @@ -75,6 +90,66 @@ public void execute(ExecutionRequest request) { private void executeAllChildren(VintageEngineDescriptor engineDescriptor, EngineExecutionListener engineExecutionListener) { + boolean parallelExecutionEnabled = getParallelExecutionEnabled(); + + if (parallelExecutionEnabled) { + executeInParallel(engineDescriptor, engineExecutionListener); + } + else { + executeSequentially(engineDescriptor, engineExecutionListener); + } + } + + private void executeInParallel(VintageEngineDescriptor engineDescriptor, + EngineExecutionListener engineExecutionListener) { + ExecutorService executorService = Executors.newFixedThreadPool(getThreadPoolSize()); + RunnerExecutor runnerExecutor = new RunnerExecutor(engineExecutionListener); + + List> futures = new ArrayList<>(); + for (Iterator iterator = engineDescriptor.getModifiableChildren().iterator(); iterator.hasNext();) { + TestDescriptor descriptor = iterator.next(); + CompletableFuture future = CompletableFuture.runAsync(() -> { + RunnerTestDescriptor testDescriptor = (RunnerTestDescriptor) descriptor; + try { + runnerExecutor.execute(testDescriptor); + } + catch (Exception e) { + engineExecutionListener.executionSkipped(testDescriptor, e.getMessage()); + } + }, executorService); + + futures.add(future); + iterator.remove(); + } + + CompletableFuture allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + try { + allOf.get(); + } + catch (InterruptedException e) { + logger.warn(e, () -> "Interruption while waiting for parallel test execution to finish"); + Thread.currentThread().interrupt(); + } + catch (ExecutionException e) { + throw ExceptionUtils.throwAsUncheckedException(e.getCause()); + } + finally { + try { + executorService.shutdown(); + if (!executorService.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + logger.warn(() -> "Executor service did not terminate within the specified timeout"); + executorService.shutdownNow(); + } + } + catch (InterruptedException e) { + logger.warn(e, () -> "Interruption while waiting for executor service to shut down"); + Thread.currentThread().interrupt(); + } + } + } + + private void executeSequentially(VintageEngineDescriptor engineDescriptor, + EngineExecutionListener engineExecutionListener) { RunnerExecutor runnerExecutor = new RunnerExecutor(engineExecutionListener); for (Iterator iterator = engineDescriptor.getModifiableChildren().iterator(); iterator.hasNext();) { runnerExecutor.execute((RunnerTestDescriptor) iterator.next()); @@ -82,4 +157,14 @@ private void executeAllChildren(VintageEngineDescriptor engineDescriptor, } } + private boolean getParallelExecutionEnabled() { + // get parallel execution enabled from configuration + return true; + } + + private int getThreadPoolSize() { + // get thread pool size from configuration + return DEFAULT_THREAD_POOL_SIZE; + } + }