From de8fbce39572c7833cd5b8bef191a81624999081 Mon Sep 17 00:00:00 2001 From: yongjunhong Date: Sun, 15 Dec 2024 12:22:15 +0900 Subject: [PATCH] Add integration test Issue: #2229 Signed-off-by: yongjunhong --- .../ParallelExecutionIntegrationTests.java | 107 ++++++++++++++++++ .../junit4/JUnit4ParallelTestCase.java | 83 ++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/ParallelExecutionIntegrationTests.java create mode 100644 junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParallelTestCase.java diff --git a/junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/ParallelExecutionIntegrationTests.java b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/ParallelExecutionIntegrationTests.java new file mode 100644 index 000000000000..f151bd86b662 --- /dev/null +++ b/junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/ParallelExecutionIntegrationTests.java @@ -0,0 +1,107 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.vintage.engine.execution; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.platform.testkit.engine.EventConditions.event; +import static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully; +import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; +import static org.junit.platform.testkit.engine.EventConditions.started; +import static org.junit.platform.testkit.engine.EventConditions.test; +import static org.junit.vintage.engine.samples.junit4.JUnit4ParallelTestCase.*; + +import java.time.Instant; +import java.util.Arrays; +import java.util.List; + +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestReporter; +import org.junit.platform.engine.discovery.ClassSelector; +import org.junit.platform.engine.discovery.DiscoverySelectors; +import org.junit.platform.launcher.LauncherDiscoveryRequest; +import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder; +import org.junit.platform.testkit.engine.EngineExecutionResults; +import org.junit.platform.testkit.engine.EngineTestKit; +import org.junit.platform.testkit.engine.Event; +import org.junit.platform.testkit.engine.Events; +import org.junit.vintage.engine.VintageTestEngine; + +class ParallelExecutionIntegrationTests { + + private static final String PARALLEL_EXECUTION_ENABLED = "junit.vintage.execution.parallel.enabled"; + private static final String PARALLEL_POOL_SIZE = "junit.vintage.execution.parallel.pool-size"; + + @Test + void successfulParallelTest(TestReporter reporter) { + var events = executeInParallelSuccessfully(3, SuccessfulParallelTestCase.class).list(); + + var startedTimestamps = getTimestampsFor(events, event(test(), started())); + var finishedTimestamps = getTimestampsFor(events, event(test(), finishedSuccessfully())); + + reporter.publishEntry("startedTimestamps", startedTimestamps.toString()); + reporter.publishEntry("finishedTimestamps", finishedTimestamps.toString()); + + assertThat(startedTimestamps).hasSize(3); + assertThat(finishedTimestamps).hasSize(3); + } + + @Test + void failingParallelTest(TestReporter reporter) { + var events = executeInParallel(3, FailingParallelTestCase.class).list(); + + var startedTimestamps = getTimestampsFor(events, event(test(), started())); + var finishedTimestamps = getTimestampsFor(events, event(test(), finishedWithFailure())); + reporter.publishEntry("startedTimestamps", startedTimestamps.toString()); + reporter.publishEntry("finishedTimestamps", finishedTimestamps.toString()); + + assertThat(startedTimestamps).hasSize(3); + assertThat(finishedTimestamps).hasSize(3); + } + + private List getTimestampsFor(List events, Condition condition) { + // @formatter:off + return events.stream() + .filter(condition::matches) + .map(Event::getTimestamp) + .toList(); + // @formatter:on + } + + private Events executeInParallelSuccessfully(int poolSize, Class... testClasses) { + var events = execute(poolSize, testClasses).allEvents(); + try { + return events.assertStatistics(it -> it.failed(0)); + } + catch (AssertionError error) { + events.debug(); + throw error; + } + } + + private Events executeInParallel(int poolSize, Class... testClasses) { + return execute(poolSize, testClasses).allEvents(); + } + + private static EngineExecutionResults execute(int poolSize, Class... testClass) { + return EngineTestKit.execute(new VintageTestEngine(), request(poolSize, testClass)); + } + + private static LauncherDiscoveryRequest request(int poolSize, Class... testClasses) { + var classSelectors = Arrays.stream(testClasses) // + .map(DiscoverySelectors::selectClass) // + .toArray(ClassSelector[]::new); + + return LauncherDiscoveryRequestBuilder.request().selectors(classSelectors).configurationParameter(PARALLEL_EXECUTION_ENABLED, + String.valueOf(true)).configurationParameter(PARALLEL_POOL_SIZE, String.valueOf(poolSize)).build(); + } + +} diff --git a/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParallelTestCase.java b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParallelTestCase.java new file mode 100644 index 000000000000..f88b400e70b3 --- /dev/null +++ b/junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParallelTestCase.java @@ -0,0 +1,83 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.vintage.engine.samples.junit4; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assert.*; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; + +@RunWith(Enclosed.class) +public class JUnit4ParallelTestCase { + + public static class SuccessfulParallelTestCase { + static AtomicInteger sharedResource; + static CountDownLatch countDownLatch; + + @BeforeClass + public static void initialize() { + sharedResource = new AtomicInteger(); + countDownLatch = new CountDownLatch(3); + } + + @Test + public void firstTest() throws Exception { + incrementAndBlock(sharedResource, countDownLatch); + } + + @Test + public void secondTest() throws Exception { + incrementAndBlock(sharedResource, countDownLatch); + } + + @Test + public void thirdTest() throws Exception { + incrementAndBlock(sharedResource, countDownLatch); + } + } + + public static class FailingParallelTestCase { + @Test + public void firstTest() { + fail("failing test"); + } + + @Test + public void secondTest() { + fail("failing test"); + } + + @Test + public void thirdTest() { + fail("failing test"); + } + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private static int incrementAndBlock(AtomicInteger sharedResource, CountDownLatch countDownLatch) + throws InterruptedException { + var value = sharedResource.incrementAndGet(); + countDownLatch.countDown(); + countDownLatch.await(estimateSimulatedTestDurationInMiliseconds(), MILLISECONDS); + return value; + } + + private static long estimateSimulatedTestDurationInMiliseconds() { + var runningInCi = Boolean.parseBoolean(System.getenv("CI")); + return runningInCi ? 1000 : 100; + } +}