Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report deadlocks when running in sharedthreadpool #3119

Merged
merged 1 commit into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Current (7.11.0)
Fixed: GITHUB-3028: Execution stalls when using "use-global-thread-pool" (Krishnan Mahadevan)

7.10.2
Fixed: GITHUB-3117: ListenerComparator doesn't work (Krishnan Mahadevan)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.testng.internal;

import org.testng.TestNGException;

/** Represents a deadlock condition in TestNG wherein execution can get stalled. */
public class TestNGDeadLockException extends TestNGException {
public TestNGDeadLockException(String string) {
super(string);
}
}
33 changes: 30 additions & 3 deletions testng-core/src/main/java/org/testng/TestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.testng.internal.TestMethodComparator;
import org.testng.internal.TestMethodContainer;
import org.testng.internal.TestNGClassFinder;
import org.testng.internal.TestNGDeadLockException;
import org.testng.internal.TestNGMethodFinder;
import org.testng.internal.Utils;
import org.testng.internal.XmlMethodSelector;
Expand All @@ -63,6 +64,7 @@
import org.testng.util.TimeUtils;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlPackage;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlTest;

/** This class takes care of running one Test. */
Expand Down Expand Up @@ -807,9 +809,34 @@ public List<IWorker<ITestNGMethod>> createWorkers(List<ITestNGMethod> methods) {
.testContext(this)
.listeners(this.m_classListeners.values())
.build();
return AbstractParallelWorker.newWorker(
m_xmlTest.getParallel(), m_xmlTest.getGroupByInstances())
.createWorkers(args);
List<IWorker<ITestNGMethod>> result =
AbstractParallelWorker.newWorker(m_xmlTest.getParallel(), m_xmlTest.getGroupByInstances())
.createWorkers(args);
long dataDrivenTestCount =
result.stream()
.flatMap(it -> it.getTasks().stream())
.filter(ITestNGMethod::isDataDriven)
.count();
int threads = getCurrentXmlTest().getThreadCount();
XmlSuite.ParallelMode parallelMode = getCurrentXmlTest().getParallel();
XmlSuite suite = getSuite().getXmlSuite();
if (suite.useGlobalThreadPool()
&& parallelMode.isParallel()
&& dataDrivenTestCount >= threads) {
String msg =
"[Deadlock condition detected] "
+ "Cannot run "
+ dataDrivenTestCount
+ " data driven tests on just "
+ threads
+ " threads when "
+ "using common thread pool. "
+ "Please increase the number of threads to at-least "
+ (dataDrivenTestCount + 1)
+ ".";
throw new TestNGDeadLockException(msg);
}
return result;
}

private void afterRun() {
Expand Down
26 changes: 26 additions & 0 deletions testng-core/src/test/java/test/thread/SharedThreadPoolTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@
import java.util.stream.Collectors;
import org.testng.TestNG;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.internal.TestNGDeadLockException;
import org.testng.xml.XmlSuite;
import test.SimpleBaseTest;
import test.thread.issue2019.TestClassSample;
import test.thread.issue3028.AnotherDataDrivenTestSample;
import test.thread.issue3028.DataDrivenTestSample;
import test.thread.issue3028.FactoryPoweredDataDrivenTestSample;

public class SharedThreadPoolTest extends SimpleBaseTest {

Expand Down Expand Up @@ -68,6 +73,27 @@ public void ensureCommonThreadPoolIsNotUsedWhenUsedWithSuiteFiles() {
.hasSizeBetween(4, 10);
}

@Test(expectedExceptions = TestNGDeadLockException.class, dataProvider = "modes")
public void ensureDeadLocksAreDetectedForDataDrivenTestsRunningInParallel(
XmlSuite.ParallelMode mode, Class<?>... classes) {
TestNG testng = create(classes);
testng.shouldUseGlobalThreadPool(true);
testng.setParallel(mode);
testng.setThreadCount(2);
testng.run();
}

@DataProvider(name = "modes")
public Object[][] parallelModes() {
return new Object[][] {
{XmlSuite.ParallelMode.METHODS, DataDrivenTestSample.class},
{XmlSuite.ParallelMode.INSTANCES, FactoryPoweredDataDrivenTestSample.class},
{
XmlSuite.ParallelMode.CLASSES, DataDrivenTestSample.class, AnotherDataDrivenTestSample.class
},
};
}

private static List<Long> runSimpleTest(String suite) {
TestNG testng = create();
testng.setTestSuites(Collections.singletonList(suite));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package test.thread.issue3028;

import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class AnotherDataDrivenTestSample {

@Test(dataProvider = "dp")
public void a(int ignored) {
log();
}

@Test(dataProvider = "dp")
public void b(int ignored) {
log();
}

private void log() {
ITestResult itr = Reporter.getCurrentTestResult();
long id = Thread.currentThread().getId();
Reporter.log("Running " + itr.toString() + " on Thread " + id);
}

@DataProvider(name = "dp", parallel = true)
public Object[][] getData() {
return new Object[][] {{1}, {2}, {3}, {4}, {5}};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package test.thread.issue3028;

import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.*;

public class DataDrivenTestSample {

@Test(dataProvider = "dp")
public void a(int ignored) {
log();
}

@Test(dataProvider = "dp")
public void b(int ignored) {
log();
}

private void log() {
ITestResult itr = Reporter.getCurrentTestResult();
long id = Thread.currentThread().getId();
Reporter.log("Running " + itr.toString() + " on Thread " + id);
}

@DataProvider(name = "dp", parallel = true)
public Object[][] getData() {
return new Object[][] {{1}, {2}, {3}, {4}, {5}};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package test.thread.issue3028;

import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;

public class FactoryPoweredDataDrivenTestSample {

@Factory
public static Object[] objects() {
return new Object[] {
new FactoryPoweredDataDrivenTestSample(), new FactoryPoweredDataDrivenTestSample()
};
}

@Test(dataProvider = "dp")
public void a(int ignored) {
log();
}

@Test(dataProvider = "dp")
public void b(int ignored) {
log();
}

private void log() {
ITestResult itr = Reporter.getCurrentTestResult();
long id = Thread.currentThread().getId();
Reporter.log("Running " + itr.toString() + " on Thread " + id);
}

@DataProvider(name = "dp", parallel = true)
public Object[][] getData() {
return new Object[][] {{1}, {2}, {3}, {4}, {5}};
}
}
Loading