Skip to content

Commit

Permalink
Streamline honoring of “singleThreaded” attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
krmahadevan committed Sep 8, 2020
1 parent 1c6cd89 commit 42f120a
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGES.txt
@@ -1,4 +1,5 @@
Current
Fixed: GITHUB-2361: No way to enforce @Test(singleThreaded = true) when test defined in base class (Krishnan Mahadevan)
Fixed: GITHUB-2343: Injectors are not reused when they share the same set of modules (Krishnan Mahadevan)
Fixed: GITHUB-2346: ITestResult attributes are null when retrieved by Listener onTestStart if test fails at BeforeMethod (Krishnan Mahadevan)
Fixed: GITHUB-2357: TestNG 7.3.0 transitive dependencies
Expand Down
22 changes: 16 additions & 6 deletions src/main/java/org/testng/internal/ClassBasedParallelWorker.java
Expand Up @@ -17,11 +17,7 @@

class ClassBasedParallelWorker extends AbstractParallelWorker {

@Override
public List<IWorker<ITestNGMethod>> createWorkers(Arguments arguments) {
List<IWorker<ITestNGMethod>> result = Lists.newArrayList();
// Methods that belong to classes with a sequential=true or parallel=classes
// attribute must all be run in the same worker
private static Set<Class<?>> gatherClassesThatShouldRunSequentially(Arguments arguments) {
Set<Class<?>> sequentialClasses = Sets.newHashSet();
for (ITestNGMethod m : arguments.getMethods()) {
Class<?> cls = m.getRealClass();
Expand All @@ -33,6 +29,15 @@ public List<IWorker<ITestNGMethod>> createWorkers(Arguments arguments) {
sequentialClasses.add(cls);
}
}
return sequentialClasses;
}

@Override
public List<IWorker<ITestNGMethod>> createWorkers(Arguments arguments) {
List<IWorker<ITestNGMethod>> result = Lists.newArrayList();
// Methods that belong to classes with a sequential=true or parallel=classes
// attribute must all be run in the same worker
Set<Class<?>> sequentialClasses = gatherClassesThatShouldRunSequentially(arguments);

List<IMethodInstance> methodInstances = Lists.newArrayList();
for (ITestNGMethod tm : arguments.getMethods()) {
Expand All @@ -49,7 +54,7 @@ public List<IWorker<ITestNGMethod>> createWorkers(Arguments arguments) {
params = getParameters(im);
prevClass = c;
}
if (sequentialClasses.contains(c)) {
if (shouldRunSequentially(c, sequentialClasses)) {
if (!processedClasses.contains(c)) {
processedClasses.add(c);
// Sequential class: all methods in one worker
Expand All @@ -67,6 +72,11 @@ public List<IWorker<ITestNGMethod>> createWorkers(Arguments arguments) {
return result;
}

private static boolean shouldRunSequentially(Class<?> c, Set<Class<?>> sequentialClasses) {
return sequentialClasses.contains(c) ||
sequentialClasses.stream().anyMatch(each -> each.isAssignableFrom(c));
}

private static List<IMethodInstance> findClasses(
List<IMethodInstance> methodInstances, Class<?> c) {
return methodInstances
Expand Down
@@ -0,0 +1,8 @@
package test.thread.issue2361;

import org.testng.annotations.Test;

@Test(singleThreaded = true)
public class AnotherChildClassExample extends BaseTestClassExample {

}
45 changes: 45 additions & 0 deletions src/test/java/test/thread/issue2361/BaseTestClassExample.java
@@ -0,0 +1,45 @@
package test.thread.issue2361;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.testng.Assert.assertEquals;

import java.util.concurrent.atomic.AtomicInteger;
import org.testng.annotations.Test;

@Test(singleThreaded = true)
public class BaseTestClassExample {

private final AtomicInteger currentTests = new AtomicInteger();

protected void test() {
int currentTests = this.currentTests.incrementAndGet();
try {
assertEquals(currentTests, 1);
MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
this.currentTests.decrementAndGet();
}
}

@Test
public void test1() {
test();
}

@Test
public void test2() {
test();
}

@Test
public void test3() {
test();
}

@Test
public void test4() {
test();
}
}
8 changes: 8 additions & 0 deletions src/test/java/test/thread/issue2361/ChildClassExample.java
@@ -0,0 +1,8 @@
package test.thread.issue2361;

import org.testng.annotations.Test;

@Test(singleThreaded = true)
public class ChildClassExample extends BaseTestClassExample {

}
15 changes: 15 additions & 0 deletions src/test/java/test/thread/issue2361/FactorySample.java
@@ -0,0 +1,15 @@
package test.thread.issue2361;

import org.testng.annotations.Factory;

public class FactorySample {

@Factory
public static Object[] newInstances() {
return new Object[] {
new ChildClassExample(),
new AnotherChildClassExample()
};
}

}
32 changes: 32 additions & 0 deletions src/test/java/test/thread/issue2361/IssueTest.java
@@ -0,0 +1,32 @@
package test.thread.issue2361;

import org.assertj.core.api.Assertions;
import org.testng.TestNG;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlSuite.ParallelMode;
import org.testng.xml.XmlTest;
import test.SimpleBaseTest;

public class IssueTest extends SimpleBaseTest {

@Test(dataProvider = "dp")
public void ensureClassLevelSingleThreadedNatureGetsHonoured(Class<?> cls) {
XmlSuite suite = createXmlSuite("Sample_Suite");
suite.setParallel(ParallelMode.METHODS);
XmlTest xmlTest = createXmlTest(suite, "Sample_Test", cls);
xmlTest.setParallel(ParallelMode.METHODS);
TestNG testng = create(suite);
testng.run();
Assertions.assertThat(testng.getStatus()).isEqualTo(0);
}

@DataProvider(name = "dp")
public Object[][] getTestData() {
return new Object[][] {
{ChildClassExample.class},
{FactorySample.class}
};
}
}
1 change: 1 addition & 0 deletions src/test/resources/testng.xml
Expand Up @@ -99,6 +99,7 @@
<class name="test.mannotation.MAnnotation2SampleTest" />
<class name="test.mannotation.issue1976.IssueTest" />
<class name="test.thread.FactoryTest" />
<class name="test.thread.issue2361.IssueTest"/>
<class name="test.thread.SequentialTest" />
<class name="test.thread.ParallelTestTest" />
<class name="test.thread.DataProviderThreadPoolSizeTest" />
Expand Down

0 comments on commit 42f120a

Please sign in to comment.