From d5fa2180e5f052e6d6a032eae3591d9aa3b5bd17 Mon Sep 17 00:00:00 2001 From: Krishnan Mahadevan Date: Mon, 9 May 2022 18:57:30 +0530 Subject: [PATCH] Streamline AfterClass invocation Closes #2726 --- CHANGES.txt | 1 + .../main/java/org/testng/ClassMethodMap.java | 3 +- .../internal/invokers/TestMethodWorker.java | 4 ++- .../test/configuration/ConfigurationTest.java | 14 +++++++++ .../issue2726/TestClassSample.java | 29 +++++++++++++++++++ 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 testng-core/src/test/java/test/configuration/issue2726/TestClassSample.java diff --git a/CHANGES.txt b/CHANGES.txt index 4a85e436d5..fb9ca2aa01 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ Current 7.6.0 +Fixed: GITHUB-2726: @AfterClass config method is executed for EACH @Test method when parallel == methods (Krishnan Mahadevan) Fixed: GITHUB-2752: TestListener is being lost when implenting both IClassListener and ITestListener (Krishnan Mahadevan) New: GITHUB-2724: DataProvider: possibility to unload dataprovider class, when done with it (Dzmitry Sankouski) Fixed: GITHUB-217: Configure TestNG to fail when there's a failure in data provider (Krishnan Mahadevan) diff --git a/testng-core/src/main/java/org/testng/ClassMethodMap.java b/testng-core/src/main/java/org/testng/ClassMethodMap.java index 9ac321726b..603b738a52 100644 --- a/testng-core/src/main/java/org/testng/ClassMethodMap.java +++ b/testng-core/src/main/java/org/testng/ClassMethodMap.java @@ -51,7 +51,8 @@ public ClassMethodMap(List methods, XmlMethodSelector xmlMethodSe public boolean removeAndCheckIfLast(ITestNGMethod m, Object instance) { Collection l = classMap.get(instance); if (l == null) { - throw new AssertionError("l should not be null"); + throw new IllegalStateException( + "Could not find any methods associated with test class instance " + instance); } l.remove(m); // It's the last method of this class if all the methods remaining in the list belong to a diff --git a/testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java b/testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java index 50cdb3b627..b224dee7f1 100644 --- a/testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java +++ b/testng-core/src/main/java/org/testng/internal/invokers/TestMethodWorker.java @@ -127,7 +127,9 @@ && doesTaskHavePreRequistes() try { invokeTestMethods(testMethod, testMthdInst.getInstance()); } finally { - invokeAfterClassMethods(testMethod.getTestClass(), testMthdInst); + synchronized (testMethod.getInstance()) { + invokeAfterClassMethods(testMethod.getTestClass(), testMthdInst); + } } } } diff --git a/testng-core/src/test/java/test/configuration/ConfigurationTest.java b/testng-core/src/test/java/test/configuration/ConfigurationTest.java index 44ccdb3213..c40b54c1e8 100644 --- a/testng-core/src/test/java/test/configuration/ConfigurationTest.java +++ b/testng-core/src/test/java/test/configuration/ConfigurationTest.java @@ -1,9 +1,13 @@ package test.configuration; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.Arrays; import org.testng.Assert; import org.testng.TestNG; import org.testng.annotations.Test; +import org.testng.xml.XmlSuite; +import test.configuration.issue2726.TestClassSample; import test.configuration.issue2743.SuiteRunnerIssueTestSample; import test.configuration.sample.ConfigurationTestSample; import test.configuration.sample.ExternalConfigurationClassSample; @@ -39,4 +43,14 @@ public void testSuiteRunnerWithDefaultConfiguration() { Assert.assertEquals(testNG.getStatus(), 0); } + + @Test(description = "GITHUB-2726") + public void testAfterClassCalledOnlyOnceForParallelTestMethods() { + TestNG testng = create(TestClassSample.class); + testng.setParallel(XmlSuite.ParallelMode.METHODS); + testng.setVerbose(2); + testng.run(); + assertThat(TestClassSample.beforeLogs).hasSize(1); + assertThat(TestClassSample.afterLogs).hasSize(1); + } } diff --git a/testng-core/src/test/java/test/configuration/issue2726/TestClassSample.java b/testng-core/src/test/java/test/configuration/issue2726/TestClassSample.java new file mode 100644 index 0000000000..f453e0931a --- /dev/null +++ b/testng-core/src/test/java/test/configuration/issue2726/TestClassSample.java @@ -0,0 +1,29 @@ +package test.configuration.issue2726; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class TestClassSample { + + public static final List beforeLogs = new CopyOnWriteArrayList<>(); + public static final List afterLogs = new CopyOnWriteArrayList<>(); + + @BeforeClass + public void setup() { + beforeLogs.add("BEFORE_" + Thread.currentThread().getName()); + } + + @AfterClass + public void tearDown() { + afterLogs.add("AFTER_" + Thread.currentThread().getName()); + } + + @Test + public void testA() {} + + @Test + public void testB() {} +}