-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Favour FutureTask for concurrency support
TestNG currently has a custom implementation of ThreadPoolExecutor named GraphThreadPoolExecutor. This class was created to facilitate concurrency in a DAG. Now that we are on JDK11, we can very well move over to leveraging FutureTask based implementations and thus decouple ourselves from the Executor and just focus on orchestrating the next node retrieval for execution. Since this is experimental, we are currently providing a JVM based switch that can fall back to the old Behaviour in case of any issues. JVM argument to use “-Dtestng.favor.custom.thread-pool.executor=true”
- Loading branch information
1 parent
3cb01b4
commit 34c7ba1
Showing
12 changed files
with
464 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
testng-core/src/main/java/org/testng/SuiteTaskExecutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package org.testng; | ||
|
||
import java.util.concurrent.BlockingQueue; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.ThreadPoolExecutor; | ||
import java.util.concurrent.TimeUnit; | ||
import org.testng.internal.IConfiguration; | ||
import org.testng.internal.RuntimeBehavior; | ||
import org.testng.internal.Utils; | ||
import org.testng.internal.thread.TestNGThreadFactory; | ||
import org.testng.internal.thread.graph.GraphOrchestrator; | ||
import org.testng.log4testng.Logger; | ||
import org.testng.thread.IExecutorFactory; | ||
import org.testng.thread.ITestNGThreadPoolExecutor; | ||
import org.testng.thread.IThreadWorkerFactory; | ||
|
||
class SuiteTaskExecutor { | ||
private final BlockingQueue<Runnable> queue; | ||
private final IDynamicGraph<ISuite> graph; | ||
private final IThreadWorkerFactory<ISuite> factory; | ||
private final IConfiguration configuration; | ||
|
||
private final int threadPoolSize; | ||
|
||
private ExecutorService service; | ||
|
||
private static final Logger LOGGER = Logger.getLogger(SuiteTaskExecutor.class); | ||
|
||
public SuiteTaskExecutor( | ||
IConfiguration configuration, | ||
IThreadWorkerFactory<ISuite> factory, | ||
BlockingQueue<Runnable> queue, | ||
IDynamicGraph<ISuite> graph, | ||
int threadPoolSize) { | ||
this.configuration = configuration; | ||
this.factory = factory; | ||
this.queue = queue; | ||
this.graph = graph; | ||
this.threadPoolSize = threadPoolSize; | ||
} | ||
|
||
public void execute() { | ||
String name = "suites-"; | ||
if (RuntimeBehavior.favourCustomThreadPoolExecutor()) { | ||
IExecutorFactory execFactory = configuration.getExecutorFactory(); | ||
ITestNGThreadPoolExecutor executor = | ||
execFactory.newSuiteExecutor( | ||
name, | ||
graph, | ||
factory, | ||
threadPoolSize, | ||
threadPoolSize, | ||
Integer.MAX_VALUE, | ||
TimeUnit.MILLISECONDS, | ||
queue, | ||
null); | ||
executor.run(); | ||
service = executor; | ||
} else { | ||
service = | ||
new ThreadPoolExecutor( | ||
threadPoolSize, | ||
threadPoolSize, | ||
Integer.MAX_VALUE, | ||
TimeUnit.MILLISECONDS, | ||
queue, | ||
new TestNGThreadFactory(name)); | ||
GraphOrchestrator<ISuite> executor = new GraphOrchestrator<>(service, factory, graph, null); | ||
executor.run(); | ||
} | ||
} | ||
|
||
public void awaitCompletion() { | ||
Utils.log("TestNG", 2, "Starting executor for all suites"); | ||
try { | ||
boolean ignored = service.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); | ||
service.shutdownNow(); | ||
} catch (InterruptedException handled) { | ||
Thread.currentThread().interrupt(); | ||
LOGGER.error(handled.getMessage(), handled); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
testng-core/src/main/java/org/testng/TestTaskExecutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
package org.testng; | ||
|
||
import java.util.Comparator; | ||
import java.util.concurrent.BlockingQueue; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.ThreadPoolExecutor; | ||
import java.util.concurrent.TimeUnit; | ||
import org.testng.internal.IConfiguration; | ||
import org.testng.internal.RuntimeBehavior; | ||
import org.testng.internal.Utils; | ||
import org.testng.internal.thread.TestNGThreadFactory; | ||
import org.testng.internal.thread.graph.GraphOrchestrator; | ||
import org.testng.log4testng.Logger; | ||
import org.testng.thread.IExecutorFactory; | ||
import org.testng.thread.ITestNGThreadPoolExecutor; | ||
import org.testng.thread.IThreadWorkerFactory; | ||
import org.testng.xml.XmlTest; | ||
|
||
class TestTaskExecutor { | ||
private final BlockingQueue<Runnable> queue; | ||
private final Comparator<ITestNGMethod> comparator; | ||
private final IDynamicGraph<ITestNGMethod> graph; | ||
private final XmlTest xmlTest; | ||
private final IThreadWorkerFactory<ITestNGMethod> factory; | ||
private final IConfiguration configuration; | ||
private final long timeOut; | ||
|
||
private ExecutorService service; | ||
|
||
private static final Logger LOGGER = Logger.getLogger(TestTaskExecutor.class); | ||
|
||
public TestTaskExecutor( | ||
IConfiguration configuration, | ||
XmlTest xmlTest, | ||
IThreadWorkerFactory<ITestNGMethod> factory, | ||
BlockingQueue<Runnable> queue, | ||
IDynamicGraph<ITestNGMethod> graph, | ||
Comparator<ITestNGMethod> comparator) { | ||
this.configuration = configuration; | ||
this.xmlTest = xmlTest; | ||
this.factory = factory; | ||
this.queue = queue; | ||
this.graph = graph; | ||
this.comparator = comparator; | ||
this.timeOut = xmlTest.getTimeOut(XmlTest.DEFAULT_TIMEOUT_MS); | ||
} | ||
|
||
public void execute() { | ||
String name = "test-" + xmlTest.getName(); | ||
int threadCount = xmlTest.getThreadCount(); | ||
threadCount = Math.max(threadCount, 1); | ||
if (RuntimeBehavior.favourCustomThreadPoolExecutor()) { | ||
IExecutorFactory execFactory = configuration.getExecutorFactory(); | ||
ITestNGThreadPoolExecutor executor = | ||
execFactory.newTestMethodExecutor( | ||
name, | ||
graph, | ||
factory, | ||
threadCount, | ||
threadCount, | ||
0, | ||
TimeUnit.MILLISECONDS, | ||
queue, | ||
comparator); | ||
executor.run(); | ||
service = executor; | ||
} else { | ||
service = | ||
new ThreadPoolExecutor( | ||
threadCount, | ||
threadCount, | ||
0, | ||
TimeUnit.MILLISECONDS, | ||
queue, | ||
new TestNGThreadFactory(name)); | ||
GraphOrchestrator<ITestNGMethod> executor = | ||
new GraphOrchestrator<>(service, factory, graph, comparator); | ||
executor.run(); | ||
} | ||
} | ||
|
||
public void awaitCompletion() { | ||
String msg = | ||
String.format( | ||
"Starting executor test %d with time out: %d milliseconds.", timeOut, timeOut); | ||
Utils.log("TestTaskExecutor", 2, msg); | ||
try { | ||
boolean ignored = service.awaitTermination(timeOut, TimeUnit.MILLISECONDS); | ||
service.shutdownNow(); | ||
} catch (InterruptedException handled) { | ||
LOGGER.error(handled.getMessage(), handled); | ||
Thread.currentThread().interrupt(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.