diff --git a/CHANGES.txt b/CHANGES.txt
index f6810ede1..bcd59d2ed 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
Current (7.10.0)
-New: GITHUB-2916: Allow users to define ordering for TestNG listeners (Krishnan Mahadevan)
+Fixed: GITHUB-3066: How to dynamically adjust the number of TestNG threads after IExecutorFactory is deprecated? (Krishnan Mahadevan)
+New: GITHUB-2874: Allow users to define ordering for TestNG listeners (Krishnan Mahadevan)
Fixed: GITHUB-3033: Moved ant support under own repository https://github.com/testng-team/testng-ant (Julien Herr)
Fixed: GITHUB-3064: TestResult lost if failure creating RetryAnalyzer (Krishnan Mahadevan)
Fixed: GITHUB-3048: ConcurrentModificationException when injecting values (Krishnan Mahadevan)
diff --git a/testng-core-api/src/main/java/org/testng/IExecutorServiceFactory.java b/testng-core-api/src/main/java/org/testng/IExecutorServiceFactory.java
new file mode 100644
index 000000000..5636e3790
--- /dev/null
+++ b/testng-core-api/src/main/java/org/testng/IExecutorServiceFactory.java
@@ -0,0 +1,35 @@
+package org.testng;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Represents the capability to create a custom {@link ExecutorService} by downstream consumers. The
+ * implementation can be plugged in via the configuration parameter -threadpoolfactoryclass
+ *
+ */
+@FunctionalInterface
+public interface IExecutorServiceFactory {
+
+ /**
+ * @param corePoolSize the number of threads to keep in the pool, even if they are idle, unless
+ * {@code allowCoreThreadTimeOut} is set
+ * @param maximumPoolSize the maximum number of threads to allow in the pool
+ * @param keepAliveTime when the number of threads is greater than the core, this is the maximum
+ * time that excess idle threads will wait for new tasks before terminating.
+ * @param unit the time unit for the {@code keepAliveTime} argument
+ * @param workQueue the queue to use for holding tasks before they are executed. This queue will
+ * hold only the {@code Runnable} tasks submitted by the {@code execute} method.
+ * @param threadFactory the factory to use when the executor creates a new thread *
+ * @return - An implementation of {@link ExecutorService}
+ */
+ ExecutorService create(
+ int corePoolSize,
+ int maximumPoolSize,
+ long keepAliveTime,
+ TimeUnit unit,
+ BlockingQueue workQueue,
+ ThreadFactory threadFactory);
+}
diff --git a/testng-core/src/main/java/org/testng/SuiteRunner.java b/testng-core/src/main/java/org/testng/SuiteRunner.java
index 7900b1663..bb6baffe1 100644
--- a/testng-core/src/main/java/org/testng/SuiteRunner.java
+++ b/testng-core/src/main/java/org/testng/SuiteRunner.java
@@ -439,7 +439,11 @@ private void runInParallelTestMode() {
}
ThreadUtil.execute(
- "tests", tasks, xmlSuite.getThreadCount(), xmlSuite.getTimeOut(XmlTest.DEFAULT_TIMEOUT_MS));
+ configuration,
+ "tests",
+ tasks,
+ xmlSuite.getThreadCount(),
+ xmlSuite.getTimeOut(XmlTest.DEFAULT_TIMEOUT_MS));
}
private class SuiteWorker implements Runnable {
diff --git a/testng-core/src/main/java/org/testng/SuiteTaskExecutor.java b/testng-core/src/main/java/org/testng/SuiteTaskExecutor.java
index 8a3f3838f..a76117fda 100644
--- a/testng-core/src/main/java/org/testng/SuiteTaskExecutor.java
+++ b/testng-core/src/main/java/org/testng/SuiteTaskExecutor.java
@@ -2,7 +2,6 @@
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;
@@ -10,8 +9,6 @@
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 {
@@ -42,29 +39,18 @@ public SuiteTaskExecutor(
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;
+ throw new UnsupportedOperationException("This is NO LONGER Supported in TestNG");
} else {
service =
- new ThreadPoolExecutor(
- threadPoolSize,
- threadPoolSize,
- Integer.MAX_VALUE,
- TimeUnit.MILLISECONDS,
- queue,
- new TestNGThreadFactory(name));
+ this.configuration
+ .getExecutorServiceFactory()
+ .create(
+ threadPoolSize,
+ threadPoolSize,
+ Integer.MAX_VALUE,
+ TimeUnit.MILLISECONDS,
+ queue,
+ new TestNGThreadFactory(name));
GraphOrchestrator executor = new GraphOrchestrator<>(service, factory, graph, null);
executor.run();
}
diff --git a/testng-core/src/main/java/org/testng/TestNG.java b/testng-core/src/main/java/org/testng/TestNG.java
index e02f7ef49..197bf2d7d 100644
--- a/testng-core/src/main/java/org/testng/TestNG.java
+++ b/testng-core/src/main/java/org/testng/TestNG.java
@@ -16,6 +16,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
@@ -58,7 +59,6 @@
import org.testng.reporters.VerboseReporter;
import org.testng.reporters.XMLReporter;
import org.testng.reporters.jq.Main;
-import org.testng.thread.IExecutorFactory;
import org.testng.thread.IThreadWorkerFactory;
import org.testng.util.Strings;
import org.testng.xml.IPostProcessor;
@@ -151,8 +151,6 @@ public class TestNG {
private final Map, IDataProviderInterceptor>
m_dataProviderInterceptors = Maps.newLinkedHashMap();
- private IExecutorFactory m_executorFactory = null;
-
public static final Integer DEFAULT_VERBOSE = 1;
// Command line suite parameters
@@ -843,10 +841,9 @@ public void setVerbose(int verbose) {
m_verbose = verbose;
}
- /** This method stands deprecated as of TestNG v7.9.0
. */
- @Deprecated
- public void setExecutorFactoryClass(String clazzName) {
- this.m_executorFactory = createExecutorFactoryInstanceUsing(clazzName);
+ public void setExecutorServiceFactory(IExecutorServiceFactory factory) {
+ Objects.requireNonNull(factory);
+ m_configuration.setExecutorServiceFactory(factory);
}
public void setListenerFactory(ITestNGListenerFactory factory) {
@@ -857,31 +854,6 @@ public void setGenerateResultsPerSuite(boolean generateResultsPerSuite) {
this.m_generateResultsPerSuite = generateResultsPerSuite;
}
- private IExecutorFactory createExecutorFactoryInstanceUsing(String clazzName) {
- Class> cls = ClassHelper.forName(clazzName);
- Object instance = m_objectFactory.newInstance(cls);
- if (instance instanceof IExecutorFactory) {
- return (IExecutorFactory) instance;
- }
- throw new IllegalArgumentException(
- clazzName + " does not implement " + IExecutorFactory.class.getName());
- }
-
- /** This method stands deprecated as of TestNG v7.9.0
. */
- @Deprecated
- public void setExecutorFactory(IExecutorFactory factory) {
- this.m_executorFactory = factory;
- }
-
- /** This method stands deprecated as of TestNG v7.9.0
. */
- @Deprecated
- public IExecutorFactory getExecutorFactory() {
- if (this.m_executorFactory == null) {
- this.m_executorFactory = createExecutorFactoryInstanceUsing(DEFAULT_THREADPOOL_FACTORY);
- }
- return this.m_executorFactory;
- }
-
private void initializeCommandLineSuites() {
if (m_commandLineTestClasses != null || m_commandLineMethods != null) {
if (null != m_commandLineMethods) {
@@ -1018,7 +990,6 @@ private void initializeConfiguration() {
m_configuration.setConfigurable(m_configurable);
m_configuration.setObjectFactory(factory);
m_configuration.setAlwaysRunListeners(this.m_alwaysRun);
- m_configuration.setExecutorFactory(getExecutorFactory());
}
private void addListeners(XmlSuite s) {
@@ -1514,9 +1485,13 @@ protected void configure(CommandLineArgs cla) {
m_objectFactory.newInstance((Class) clazz));
}
}
- if (cla.threadPoolFactoryClass != null) {
- setExecutorFactoryClass(cla.threadPoolFactoryClass);
- }
+ Optional.ofNullable(cla.threadPoolFactoryClass)
+ .map(ClassHelper::forName)
+ .filter(IExecutorServiceFactory.class::isAssignableFrom)
+ .map(it -> m_objectFactory.newInstance(it))
+ .map(it -> (IExecutorServiceFactory) it)
+ .ifPresent(this::setExecutorServiceFactory);
+
setOutputDirectory(cla.outputDirectory);
String testClasses = cla.testClass;
diff --git a/testng-core/src/main/java/org/testng/TestTaskExecutor.java b/testng-core/src/main/java/org/testng/TestTaskExecutor.java
index 110d8fe92..56d5273d3 100644
--- a/testng-core/src/main/java/org/testng/TestTaskExecutor.java
+++ b/testng-core/src/main/java/org/testng/TestTaskExecutor.java
@@ -3,7 +3,6 @@
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 java.util.function.Supplier;
import org.testng.internal.IConfiguration;
@@ -13,8 +12,6 @@
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;
@@ -51,31 +48,21 @@ public void execute() {
String name = "test-" + xmlTest.getName();
int threadCount = Math.max(xmlTest.getThreadCount(), 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;
+ throw new UnsupportedOperationException("This is NO LONGER Supported in TestNG");
+
} else {
boolean reUse = xmlTest.getSuite().useGlobalThreadPool();
Supplier