Skip to content

Commit b937a1d

Browse files
committed
Wire-In listeners consistently
Closes #2752
1 parent e3fe52a commit b937a1d

File tree

7 files changed

+159
-25
lines changed

7 files changed

+159
-25
lines changed

CHANGES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Current
22
7.6.0
3-
New: GITHUB-2724: DataProvider: possibility to unload dataprovider class, when done with it (Dzmitry Sankouski)
3+
Fixed: GITHUB-2752: TestListener is being lost when implenting both IClassListener and ITestListener (Krishnan Mahadevan)
4+
New: GITHUB-2724: DataProvider: possibility to unload dataprovider class, when done with it (Dzmitry Sankouski)
45
Fixed: GITHUB-217: Configure TestNG to fail when there's a failure in data provider (Krishnan Mahadevan)
56
Fixed: GITHUB-2743: SuiteRunner could not be initial by default Configuration (Nan Liang)
67
Fixed: GITHUB-2729: beforeConfiguration() listener method should be invoked for skipped configurations as well(Nan Liang)

testng-core/src/main/java/org/testng/TestRunner.java

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
import java.util.concurrent.PriorityBlockingQueue;
1818
import java.util.concurrent.TimeUnit;
1919
import java.util.concurrent.atomic.AtomicReference;
20+
import java.util.stream.Collectors;
2021
import java.util.stream.Stream;
2122
import javax.annotation.Nonnull;
2223
import org.testng.collections.Lists;
2324
import org.testng.collections.Maps;
2425
import org.testng.collections.Sets;
2526
import org.testng.internal.Attributes;
27+
import org.testng.internal.ClassBasedWrapper;
2628
import org.testng.internal.ClassInfoMap;
2729
import org.testng.internal.ConfigurationGroupMethods;
2830
import org.testng.internal.DefaultListenerFactory;
@@ -374,11 +376,7 @@ private void initListeners() {
374376

375377
// Instantiate all the listeners
376378
for (Class<? extends ITestNGListener> c : listenerClasses) {
377-
if (IClassListener.class.isAssignableFrom(c) && m_classListeners.containsKey(c)) {
378-
continue;
379-
}
380379
ITestNGListener listener = factory.createListener(c);
381-
382380
addListener(listener);
383381
}
384382
}
@@ -1119,20 +1117,11 @@ public List<ITestListener> getTestListeners() {
11191117

11201118
@Override
11211119
public List<IConfigurationListener> getConfigurationListeners() {
1122-
List<IConfigurationListener> listeners = Lists.newArrayList(m_configurationListeners);
1123-
for (IConfigurationListener each : this.m_configuration.getConfigurationListeners()) {
1124-
boolean duplicate = false;
1125-
for (IConfigurationListener listener : listeners) {
1126-
if (each.getClass().equals(listener.getClass())) {
1127-
duplicate = true;
1128-
break;
1129-
}
1130-
}
1131-
if (!duplicate) {
1132-
listeners.add(each);
1133-
}
1134-
}
1135-
return Lists.newArrayList(listeners);
1120+
return m_configurationListeners.stream()
1121+
.map(ClassBasedWrapper::wrap)
1122+
.distinct()
1123+
.map(ClassBasedWrapper::unWrap)
1124+
.collect(Collectors.toUnmodifiableList());
11361125
}
11371126

11381127
private void logFailedTest(ITestResult tr, boolean withinSuccessPercentage) {
@@ -1170,7 +1159,6 @@ void addTestListener(ITestListener listener) {
11701159
}
11711160

11721161
public void addListener(ITestNGListener listener) {
1173-
// TODO a listener may be added many times if it implements many interfaces
11741162
if (listener instanceof IMethodInterceptor) {
11751163
m_methodInterceptors.add((IMethodInterceptor) listener);
11761164
}
@@ -1180,9 +1168,7 @@ public void addListener(ITestNGListener listener) {
11801168
}
11811169
if (listener instanceof IClassListener) {
11821170
IClassListener classListener = (IClassListener) listener;
1183-
if (!m_classListeners.containsKey(classListener.getClass())) {
1184-
m_classListeners.put(classListener.getClass(), classListener);
1185-
}
1171+
m_classListeners.putIfAbsent(classListener.getClass(), classListener);
11861172
}
11871173
if (listener instanceof IConfigurationListener) {
11881174
addConfigurationListener((IConfigurationListener) listener);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.testng.internal;
2+
3+
import java.util.Objects;
4+
5+
public final class ClassBasedWrapper<T> {
6+
7+
private final T object;
8+
9+
private ClassBasedWrapper(T object) {
10+
this.object = object;
11+
}
12+
13+
public static <T> ClassBasedWrapper<T> wrap(T object) {
14+
return new ClassBasedWrapper<>(object);
15+
}
16+
17+
public T unWrap() {
18+
return object;
19+
}
20+
21+
@Override
22+
public boolean equals(Object o) {
23+
if (this == o) return true;
24+
if (o == null || getClass() != o.getClass()) return false;
25+
ClassBasedWrapper<?> wrapper = (ClassBasedWrapper<?>) o;
26+
return object.getClass().equals(wrapper.getClass());
27+
}
28+
29+
@Override
30+
public int hashCode() {
31+
return Objects.hash(object);
32+
}
33+
}

testng-core/src/test/java/test/listeners/ListenerInXmlTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package test.listeners;
22

3-
import java.util.Arrays;
3+
import java.util.List;
44
import org.testng.Assert;
55
import org.testng.TestNG;
66
import org.testng.annotations.Test;
@@ -11,7 +11,7 @@ public class ListenerInXmlTest extends SimpleBaseTest {
1111
@Test(description = "Make sure that listeners defined in testng.xml are invoked")
1212
public void listenerInXmlShouldBeInvoked() {
1313
TestNG tng = create();
14-
tng.setTestSuites(Arrays.asList(getPathToResource("listener-in-xml.xml")));
14+
tng.setTestSuites(List.of(getPathToResource("listener-in-xml.xml")));
1515
LListener.invoked = false;
1616
tng.run();
1717
Assert.assertTrue(LListener.invoked);

testng-core/src/test/java/test/listeners/ListenersTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.Arrays;
99
import java.util.Collections;
1010
import java.util.HashMap;
11+
import java.util.List;
1112
import java.util.Map;
1213
import org.assertj.core.api.Assertions;
1314
import org.testng.TestNG;
@@ -20,6 +21,8 @@
2021
import test.listeners.issue2638.TestClassBSample;
2122
import test.listeners.issue2685.InterruptedTestSample;
2223
import test.listeners.issue2685.SampleTestFailureListener;
24+
import test.listeners.issue2752.ListenerSample;
25+
import test.listeners.issue2752.TestClassSample;
2326

2427
public class ListenersTest extends SimpleBaseTest {
2528

@@ -67,6 +70,42 @@ public Object[][] getSuites() throws IOException {
6770
};
6871
}
6972

73+
@Test(description = "GITHUB-2752")
74+
public void testWiringInOfListenersInMultipleTestTagsWithNoListenerInSuite() {
75+
setupTest(false);
76+
List<String> expected =
77+
Arrays.asList(
78+
"onStart", "onBeforeClass", "onTestStart", "onTestSuccess", "onAfterClass", "onFinish");
79+
Map<String, List<String>> logs = ListenerSample.getLogs();
80+
assertThat(logs.get("Xml_Test_1")).containsAll(expected);
81+
assertThat(logs.get("Xml_Test_2")).containsAll(expected);
82+
}
83+
84+
@Test(description = "GITHUB-2752")
85+
public void testWiringInOfListenersInMultipleTestTagsWithListenerInSuite() {
86+
setupTest(true);
87+
List<String> expected =
88+
Arrays.asList(
89+
"onStart", "onBeforeClass", "onTestStart", "onTestSuccess", "onAfterClass", "onFinish");
90+
Map<String, List<String>> logs = ListenerSample.getLogs();
91+
assertThat(logs.get("Xml_Test_1")).containsAll(expected);
92+
assertThat(logs.get("Xml_Test_2")).containsAll(expected);
93+
}
94+
95+
private void setupTest(boolean addExplicitListener) {
96+
TestNG testng = new TestNG();
97+
XmlSuite xmlSuite = createXmlSuite("Xml_Suite");
98+
createXmlTest(xmlSuite, "Xml_Test_1", TestClassSample.class);
99+
createXmlTest(xmlSuite, "Xml_Test_2", TestClassSample.class);
100+
testng.setXmlSuites(Collections.singletonList(xmlSuite));
101+
testng.setVerbose(2);
102+
if (addExplicitListener) {
103+
ListenerSample listener = new ListenerSample();
104+
testng.addListener(listener);
105+
}
106+
testng.run();
107+
}
108+
70109
private static Map<String, String[]> getExpectations() {
71110
Map<String, String[]> expected = new HashMap<>();
72111
expected.put("Container_Suite", new String[] {});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package test.listeners.issue2752;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.concurrent.ConcurrentHashMap;
7+
import org.testng.IClassListener;
8+
import org.testng.ITestClass;
9+
import org.testng.ITestContext;
10+
import org.testng.ITestListener;
11+
import org.testng.ITestResult;
12+
13+
public class ListenerSample implements IClassListener, ITestListener {
14+
15+
private static final Map<String, List<String>> logs = new ConcurrentHashMap<>();
16+
17+
public ListenerSample() {
18+
logs.clear();
19+
}
20+
21+
public static Map<String, List<String>> getLogs() {
22+
return logs;
23+
}
24+
25+
@Override
26+
public void onBeforeClass(ITestClass testClass) {
27+
String key = testClass.getXmlTest().getName();
28+
logs.computeIfAbsent(key, k -> new ArrayList<>()).add("onBeforeClass");
29+
}
30+
31+
@Override
32+
public void onAfterClass(ITestClass testClass) {
33+
String key = testClass.getXmlTest().getName();
34+
logs.computeIfAbsent(key, k -> new ArrayList<>()).add("onAfterClass");
35+
}
36+
37+
@Override
38+
public void onTestStart(ITestResult result) {
39+
String key = result.getTestContext().getName();
40+
logs.computeIfAbsent(key, k -> new ArrayList<>()).add("onTestStart");
41+
}
42+
43+
@Override
44+
public void onTestSuccess(ITestResult result) {
45+
String key = result.getTestContext().getName();
46+
logs.computeIfAbsent(key, k -> new ArrayList<>()).add("onTestSuccess");
47+
}
48+
49+
@Override
50+
public void onFinish(ITestContext context) {
51+
String key = context.getName();
52+
logs.computeIfAbsent(key, k -> new ArrayList<>()).add("onFinish");
53+
}
54+
55+
@Override
56+
public void onStart(ITestContext context) {
57+
String key = context.getName();
58+
logs.computeIfAbsent(key, k -> new ArrayList<>()).add("onStart");
59+
}
60+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package test.listeners.issue2752;
2+
3+
import org.testng.annotations.BeforeClass;
4+
import org.testng.annotations.Listeners;
5+
import org.testng.annotations.Test;
6+
7+
@Listeners(ListenerSample.class)
8+
public class TestClassSample {
9+
10+
@Test
11+
public void testMethod() {}
12+
13+
@BeforeClass
14+
public void beforeClass() {}
15+
}

0 commit comments

Comments
 (0)