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

Support ITestNGFactory customisation #3060

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.txt
@@ -1,5 +1,6 @@
Current
7.10.0
Fixed: GITHUB-3059: Support the ability to inject custom listener factory (Krishnan Mahadevan)
Fixed: GITHUB-3038: java.lang.IllegalStateException: Results per method should NOT have been empty (Krishnan Mahadevan)
Fixed: GITHUB-3022: Remove deprecated JUnit related support in TestNG (Krishnan Mahadevan)

Expand Down
5 changes: 5 additions & 0 deletions testng-core/src/main/java/org/testng/CommandLineArgs.java
Expand Up @@ -166,6 +166,11 @@ public class CommandLineArgs {
description = "The factory used to create tests")
public String testRunnerFactory;

public static final String LISTENER_FACTORY = "-listenerfactory";

@Parameter(names = LISTENER_FACTORY, description = "The factory used to create TestNG listeners")
public String listenerFactory;

public static final String METHODS = "-methods";

@Parameter(names = METHODS, description = "Comma separated of test methods")
Expand Down
18 changes: 17 additions & 1 deletion testng-core/src/main/java/org/testng/TestNG.java
Expand Up @@ -26,6 +26,7 @@
import org.testng.collections.Sets;
import org.testng.internal.ClassHelper;
import org.testng.internal.Configuration;
import org.testng.internal.DefaultListenerFactory;
import org.testng.internal.DynamicGraph;
import org.testng.internal.ExitCode;
import org.testng.internal.IConfiguration;
Expand Down Expand Up @@ -682,8 +683,12 @@ public void setObjectFactory(ITestObjectFactory factory) {
* IReporter
*/
public void setListenerClasses(List<Class<? extends ITestNGListener>> classes) {
ITestNGListenerFactory factory = m_configuration.getListenerFactory();
if (factory == null) {
factory = new DefaultListenerFactory(m_objectFactory, null);
}
for (Class<? extends ITestNGListener> cls : classes) {
addListener(m_objectFactory.newInstance(cls));
addListener(factory.createListener(cls));
}
}

Expand Down Expand Up @@ -835,6 +840,10 @@ public void setExecutorFactoryClass(String clazzName) {
this.m_executorFactory = createExecutorFactoryInstanceUsing(clazzName);
}

public void setListenerFactory(ITestNGListenerFactory factory) {
this.m_configuration.setListenerFactory(factory);
}

public void setGenerateResultsPerSuite(boolean generateResultsPerSuite) {
this.m_generateResultsPerSuite = generateResultsPerSuite;
}
Expand Down Expand Up @@ -1466,6 +1475,13 @@ protected void configure(CommandLineArgs cla) {
.ifPresent(value -> propagateDataProviderFailureAsTestFailure());
setReportAllDataDrivenTestsAsSkipped(cla.includeAllDataDrivenTestsWhenSkipping);

Optional.ofNullable(cla.listenerFactory)
.map(ClassHelper::forName)
.filter(ITestNGListenerFactory.class::isAssignableFrom)
.map(it -> m_objectFactory.newInstance(it))
.map(it -> (ITestNGListenerFactory) it)
.ifPresent(this::setListenerFactory);
krmahadevan marked this conversation as resolved.
Show resolved Hide resolved

Optional.ofNullable(cla.generateResultsPerSuite).ifPresent(this::setGenerateResultsPerSuite);

if (cla.verbose != null) {
Expand Down
5 changes: 3 additions & 2 deletions testng-core/src/main/java/org/testng/TestRunner.java
Expand Up @@ -11,6 +11,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
Expand Down Expand Up @@ -377,8 +378,8 @@ private void initListeners() {
//

ITestNGListenerFactory factory =
TestListenerHelper.createListenerFactory(
m_objectFactory, m_testClassFinder, listenerFactoryClass, this);
Optional.ofNullable(m_configuration.getListenerFactory())
.orElse(new DefaultListenerFactory(m_objectFactory, this));

// Instantiate all the listeners
for (Class<? extends ITestNGListener> c : listenerClasses) {
Expand Down
Expand Up @@ -7,6 +7,7 @@
import org.testng.IExecutionListener;
import org.testng.IHookable;
import org.testng.IInjectorFactory;
import org.testng.ITestNGListenerFactory;
import org.testng.ITestObjectFactory;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
Expand All @@ -24,6 +25,8 @@ public class Configuration implements IConfiguration {
private IHookable m_hookable;
private IConfigurable m_configurable;

private ITestNGListenerFactory m_listenerFactory;

private boolean shareThreadPoolForDataProviders = false;
private final Map<Class<? extends IExecutionListener>, IExecutionListener> m_executionListeners =
Maps.newLinkedHashMap();
Expand Down Expand Up @@ -63,6 +66,16 @@ public void setAnnotationFinder(IAnnotationFinder finder) {
m_annotationFinder = finder;
}

@Override
public void setListenerFactory(ITestNGListenerFactory testNGListenerFactory) {
this.m_listenerFactory = testNGListenerFactory;
}

@Override
public ITestNGListenerFactory getListenerFactory() {
return m_listenerFactory;
}

@Override
public ITestObjectFactory getObjectFactory() {
return m_objectFactory;
Expand Down
Expand Up @@ -10,6 +10,10 @@ public interface IConfiguration {

void setAnnotationFinder(IAnnotationFinder finder);

void setListenerFactory(ITestNGListenerFactory testNGListenerFactory);

ITestNGListenerFactory getListenerFactory();

ITestObjectFactory getObjectFactory();

void setObjectFactory(ITestObjectFactory m_objectFactory);
Expand Down
Expand Up @@ -161,6 +161,8 @@ public static ListenerHolder findAllListeners(Class<?> cls, IAnnotationFinder fi
return result;
}

/** @deprecated - This method stands deprecated as of TestNG version <code>7.10.0</code> */
@Deprecated
public static ITestNGListenerFactory createListenerFactory(
ITestObjectFactory objectFactory,
TestNGClassFinder finder,
Expand Down
@@ -1,6 +1,5 @@
package org.testng.internal.objects.pojo;

import java.util.Objects;
import org.testng.ITestContext;
import org.testng.internal.invokers.objects.GuiceContext;

Expand All @@ -13,7 +12,6 @@ public class CreationAttributes {
private final GuiceContext suiteContext;

public CreationAttributes(ITestContext ctx, BasicAttributes basic, DetailedAttributes detailed) {
Objects.requireNonNull(ctx);
krmahadevan marked this conversation as resolved.
Show resolved Hide resolved
this.basic = basic;
this.detailed = detailed;
this.context = ctx;
Expand Down
@@ -0,0 +1,18 @@
package test.listeners.factory;

import org.testng.IExecutionListener;

public class ExampleListener implements IExecutionListener {

public static ExampleListener instance;

public ExampleListener() {
setInstance(this);
}

private static synchronized void setInstance(ExampleListener instance) {
if (ExampleListener.instance == null) {
ExampleListener.instance = instance;
}
}
}
@@ -0,0 +1,8 @@
package test.listeners.factory;

import org.testng.annotations.Test;

public class SampleTestCase {
@Test
public void testMethod() {}
}
@@ -0,0 +1,30 @@
package test.listeners.factory;

import org.testng.ITestNGListener;
import org.testng.ITestNGListenerFactory;
import org.testng.internal.objects.InstanceCreator;

public class SampleTestFactory implements ITestNGListenerFactory {

public static ITestNGListenerFactory instance;

private boolean invoked = false;

public boolean isInvoked() {
return invoked;
}

public SampleTestFactory() {
setInstance(this);
}

private static void setInstance(ITestNGListenerFactory instance) {
SampleTestFactory.instance = instance;
}

@Override
public ITestNGListener createListener(Class<? extends ITestNGListener> listenerClass) {
invoked = true;
return InstanceCreator.newInstance(listenerClass);
}
}
@@ -0,0 +1,41 @@
package test.listeners.factory;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import java.util.List;
import org.testng.CommandLineArgs;
import org.testng.TestNG;
import org.testng.annotations.Test;
import test.SimpleBaseTest;

public class TestNGFactoryTest extends SimpleBaseTest {

@Test(description = "GITHUB-3059")
public void testListenerFactoryViaConfigurationArg() {
String[] args =
new String[] {
CommandLineArgs.LISTENER_FACTORY,
SampleTestFactory.class.getName(),
CommandLineArgs.TEST_CLASS,
SampleTestCase.class.getName(),
CommandLineArgs.LISTENER,
ExampleListener.class.getName()
};
TestNG testng = TestNG.privateMain(args, null);
assertThat(SampleTestFactory.instance).isNotNull();
assertThat(ExampleListener.instance).isNotNull();
assertThat(testng.getStatus()).isZero();
}

@Test(description = "GITHUB-3059")
public void testListenerFactoryViaTestNGApi() {
TestNG testng = new TestNG();
SampleTestFactory factory = new SampleTestFactory();
testng.setListenerFactory(factory);
testng.setListenerClasses(List.of(ExampleListener.class));
testng.setTestClasses(new Class[] {SampleTestCase.class});
testng.run();
assertThat(testng.getStatus()).isZero();
assertThat(factory.isInvoked()).isTrue();
}
}
1 change: 1 addition & 0 deletions testng-core/src/test/resources/testng.xml
Expand Up @@ -192,6 +192,7 @@
<class name="test.skip.github1632.IssueTest"/>
<class name="test.listeners.issue2220.IssueTest"/>
<class name="test.listeners.issue2328.IssueTest"/>
<class name="test.listeners.factory.TestNGFactoryTest"/>
<class name="test_result.ResultStatusTest"/>
<class name="test_result.TestResultTest"/>
<class name="test.listeners.github1319.TestResultInstanceCheckTest"/>
Expand Down