diff --git a/CHANGES.txt b/CHANGES.txt
index a3e458e39..459f17c18 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
Current (7.10.0)
+Fixed: GITHUB-2381: Controlling the inclusion of the listener at runtime (Krishnan Mahadevan)
Fixed: GITHUB-3082: IInvokedMethodListener Iinvoked method does not return correct instance during @BeforeMethod, @AfterMethod and @AfterClass (Krishnan Mahadevan)
Fixed: GITHUB-3084: Document project's PGP artifact signing keys (Krishnan Mahadevan)
Fixed: GITHUB-3079: Associate a unique id with every test class object instantiated by TestNG (Krishnan Mahadevan)
diff --git a/testng-core-api/src/main/java/org/testng/ITestNGListener.java b/testng-core-api/src/main/java/org/testng/ITestNGListener.java
index b167ac595..727f031d5 100644
--- a/testng-core-api/src/main/java/org/testng/ITestNGListener.java
+++ b/testng-core-api/src/main/java/org/testng/ITestNGListener.java
@@ -5,4 +5,10 @@
*
* @author cbeust
*/
-public interface ITestNGListener {}
+public interface ITestNGListener {
+
+ /** @return - true
if the current listener can be considered for execution. */
+ default boolean isEnabled() {
+ return true;
+ }
+}
diff --git a/testng-core/src/main/java/org/testng/TestNG.java b/testng-core/src/main/java/org/testng/TestNG.java
index cf188cc26..b3938e0db 100644
--- a/testng-core/src/main/java/org/testng/TestNG.java
+++ b/testng-core/src/main/java/org/testng/TestNG.java
@@ -731,6 +731,9 @@ public void addListener(ITestNGListener listener) {
if (listener == null) {
return;
}
+ if (!listener.isEnabled()) {
+ return;
+ }
if (listener instanceof IExecutionVisualiser) {
IExecutionVisualiser visualiser = (IExecutionVisualiser) listener;
maybeAddListener(m_executionVisualisers, visualiser);
diff --git a/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java b/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java
index 5121faae4..2b1f776e6 100644
--- a/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java
+++ b/testng-core/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java
@@ -159,6 +159,9 @@ private void transform(
Constructor> testConstructor,
Method testMethod,
Class> whichClass) {
+ if (!m_transformer.isEnabled()) {
+ return;
+ }
//
// Transform @Test
//
diff --git a/testng-core/src/test/java/test/listeners/ListenersTest.java b/testng-core/src/test/java/test/listeners/ListenersTest.java
index 9b5b6e4d2..70e5235d1 100644
--- a/testng-core/src/test/java/test/listeners/ListenersTest.java
+++ b/testng-core/src/test/java/test/listeners/ListenersTest.java
@@ -19,6 +19,9 @@
import org.testng.internal.ExitCode;
import org.testng.xml.XmlSuite;
import test.SimpleBaseTest;
+import test.listeners.issue2381.FactoryTestClassSample;
+import test.listeners.issue2381.SampleGlobalListener;
+import test.listeners.issue2381.SampleTransformer;
import test.listeners.issue2638.DummyInvokedMethodListener;
import test.listeners.issue2638.TestClassASample;
import test.listeners.issue2638.TestClassBSample;
@@ -524,6 +527,49 @@ public void testSkipStatusInBeforeAndAfterConfigurationMethod(
assertThat(listener.getLogs()).containsExactlyElementsOf(expected);
}
+ @Test(description = "GITHUB-2381")
+ public void ensureListenersCanBeDisabled() {
+ SampleGlobalListener.clearLogs();
+ SampleTransformer.clearLogs();
+ TestNG testng =
+ create(test.listeners.issue2381.TestClassSample.class, FactoryTestClassSample.class);
+ SampleGlobalListener listener = new SampleGlobalListener();
+ SampleTransformer transformer = new SampleTransformer();
+ testng.addListener(listener);
+ testng.addListener(transformer);
+ testng.run();
+ assertThat(SampleGlobalListener.getLogs()).isEmpty();
+ assertThat(SampleTransformer.getLogs()).isEmpty();
+ }
+
+ @Test(description = "GITHUB-2381")
+ public void ensureListenersCanBeDisabledViaCLI() {
+ SampleGlobalListener.clearLogs();
+ SampleTransformer.clearLogs();
+ String listeners =
+ String.join(",", SampleGlobalListener.class.getName(), SampleTransformer.class.getName());
+ String testClasses =
+ String.join(
+ ",",
+ test.listeners.issue2381.TestClassSample.class.getName(),
+ FactoryTestClassSample.class.getName());
+ List args = List.of("-listener", listeners, "-testclass", testClasses);
+ TestNG.privateMain(args.toArray(String[]::new), null);
+ assertThat(SampleGlobalListener.getLogs()).isEmpty();
+ assertThat(SampleTransformer.getLogs()).isEmpty();
+ }
+
+ @Test(description = "GITHUB-2381")
+ public void ensureListenersCanBeDisabledViaSuiteFile() {
+ SampleGlobalListener.clearLogs();
+ SampleTransformer.clearLogs();
+ TestNG testng = new TestNG();
+ testng.setTestSuites(List.of("src/test/resources/2381.xml"));
+ testng.run();
+ assertThat(SampleGlobalListener.getLogs()).isEmpty();
+ assertThat(SampleTransformer.getLogs()).isEmpty();
+ }
+
@Test(description = "GITHUB-3082")
public void ensureListenerWorksWithCorrectTestClassInstance() {
TestNG tng = create(test.listeners.issue3082.TestClassSample.class);
diff --git a/testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java b/testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java
new file mode 100644
index 000000000..cd03ff822
--- /dev/null
+++ b/testng-core/src/test/java/test/listeners/issue2381/FactoryTestClassSample.java
@@ -0,0 +1,27 @@
+package test.listeners.issue2381;
+
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+
+public class FactoryTestClassSample {
+
+ @Factory(dataProvider = "dp")
+ public FactoryTestClassSample(int ignored) {}
+
+ @DataProvider
+ public static Object[][] dp() {
+ return new Object[][] {{1}};
+ }
+
+ @BeforeSuite
+ public void beforeSuite() {}
+
+ @Test
+ public void testMethod() {}
+
+ @AfterSuite
+ public void afterSuite() {}
+}
diff --git a/testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java b/testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java
new file mode 100644
index 000000000..b50e97b94
--- /dev/null
+++ b/testng-core/src/test/java/test/listeners/issue2381/SampleGlobalListener.java
@@ -0,0 +1,141 @@
+package test.listeners.issue2381;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.testng.IAlterSuiteListener;
+import org.testng.IClassListener;
+import org.testng.IExecutionListener;
+import org.testng.IExecutionVisualiser;
+import org.testng.IInvokedMethod;
+import org.testng.IInvokedMethodListener;
+import org.testng.IReporter;
+import org.testng.ISuite;
+import org.testng.ISuiteListener;
+import org.testng.ITestClass;
+import org.testng.ITestContext;
+import org.testng.ITestListener;
+import org.testng.ITestResult;
+import org.testng.xml.XmlSuite;
+
+public class SampleGlobalListener
+ implements IExecutionListener,
+ IAlterSuiteListener,
+ ISuiteListener,
+ ITestListener,
+ IInvokedMethodListener,
+ IClassListener,
+ IExecutionVisualiser,
+ IReporter {
+
+ private static final List logs = new ArrayList<>();
+
+ public static List getLogs() {
+ return Collections.unmodifiableList(logs);
+ }
+
+ public static void clearLogs() {
+ logs.clear();
+ }
+
+ @Override
+ public void onExecutionStart() {
+ logs.add("onExecutionStart");
+ }
+
+ @Override
+ public void alter(List suites) {
+ logs.add("alter");
+ }
+
+ @Override
+ public void onStart(ISuite suite) {
+ logs.add("onStart(ISuite)");
+ }
+
+ @Override
+ public void onStart(ITestContext context) {
+ logs.add("onStart(ITestContext)");
+ }
+
+ @Override
+ public void onBeforeClass(ITestClass testClass) {
+ logs.add("onBeforeClass");
+ }
+
+ @Override
+ public void consumeDotDefinition(String dotDefinition) {
+ logs.add("consumeDotDefinition");
+ }
+
+ @Override
+ public void onTestStart(ITestResult result) {
+ logs.add("onTestStart(ITestResult)");
+ }
+
+ @Override
+ public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
+ logs.add("beforeInvocation");
+ }
+
+ @Override
+ public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
+ logs.add("afterInvocation");
+ }
+
+ @Override
+ public void onTestSuccess(ITestResult result) {
+ logs.add("onTestSuccess(ITestResult)");
+ }
+
+ @Override
+ public void onTestFailure(ITestResult result) {
+ logs.add("onTestFailure(ITestResult)");
+ }
+
+ @Override
+ public void onTestSkipped(ITestResult result) {
+ logs.add("onTestSkipped(ITestResult)");
+ }
+
+ @Override
+ public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
+ logs.add("onTestFailedButWithinSuccessPercentage(ITestResult)");
+ }
+
+ @Override
+ public void onTestFailedWithTimeout(ITestResult result) {
+ logs.add("onTestFailedWithTimeout(ITestResult)");
+ }
+
+ @Override
+ public void onAfterClass(ITestClass testClass) {
+ logs.add("onAfterClass");
+ }
+
+ @Override
+ public void onFinish(ITestContext context) {
+ logs.add("onFinish(ITestContext)");
+ }
+
+ @Override
+ public void onFinish(ISuite suite) {
+ logs.add("onFinish(ISuite)");
+ }
+
+ @Override
+ public void generateReport(
+ List xmlSuites, List suites, String outputDirectory) {
+ logs.add("generateReport");
+ }
+
+ @Override
+ public void onExecutionFinish() {
+ logs.add("onExecutionFinish");
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+}
diff --git a/testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java b/testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java
new file mode 100644
index 000000000..ce0ea9a42
--- /dev/null
+++ b/testng-core/src/test/java/test/listeners/issue2381/SampleTransformer.java
@@ -0,0 +1,61 @@
+package test.listeners.issue2381;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.testng.IAnnotationTransformer;
+import org.testng.annotations.IConfigurationAnnotation;
+import org.testng.annotations.IDataProviderAnnotation;
+import org.testng.annotations.IFactoryAnnotation;
+import org.testng.annotations.IListenersAnnotation;
+import org.testng.annotations.ITestAnnotation;
+
+public class SampleTransformer implements IAnnotationTransformer {
+
+ private static final List logs = new ArrayList<>();
+
+ public static List getLogs() {
+ return Collections.unmodifiableList(logs);
+ }
+
+ public static void clearLogs() {
+ logs.clear();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+
+ @Override
+ public void transform(IListenersAnnotation annotation, Class> testClass) {
+ logs.add("transform_listener");
+ }
+
+ @Override
+ public void transform(IDataProviderAnnotation annotation, Method method) {
+ logs.add("transform_data_provider");
+ }
+
+ @Override
+ public void transform(IFactoryAnnotation annotation, Method method) {
+ logs.add("transform_factory");
+ }
+
+ @Override
+ public void transform(
+ IConfigurationAnnotation annotation,
+ Class testClass,
+ Constructor testConstructor,
+ Method testMethod) {
+ logs.add("transform_configuration");
+ }
+
+ @Override
+ public void transform(
+ ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
+ logs.add("transform_test");
+ }
+}
diff --git a/testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java b/testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java
new file mode 100644
index 000000000..3c1606853
--- /dev/null
+++ b/testng-core/src/test/java/test/listeners/issue2381/TestClassSample.java
@@ -0,0 +1,18 @@
+package test.listeners.issue2381;
+
+import org.testng.IExecutionListener;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Listeners;
+import org.testng.annotations.Test;
+
+@Listeners(TestClassSample.MyListener.class)
+public class TestClassSample {
+
+ @BeforeClass
+ public void beforeClass() {}
+
+ @Test
+ public void testMethod() {}
+
+ public static class MyListener implements IExecutionListener {}
+}
diff --git a/testng-core/src/test/resources/2381.xml b/testng-core/src/test/resources/2381.xml
new file mode 100644
index 000000000..cc47de268
--- /dev/null
+++ b/testng-core/src/test/resources/2381.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+