Skip to content

Commit 94c4d2b

Browse files
committed
Streamline data provider listener invocation
Closes #3045
1 parent 4c7653d commit 94c4d2b

File tree

6 files changed

+127
-4
lines changed

6 files changed

+127
-4
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Current
22
7.10.0
33
Fixed: GITHUB-3059: Support the ability to inject custom listener factory (Krishnan Mahadevan)
4+
Fixed: GITHUB-3045: IDataProviderListener - beforeDataProviderExecution and afterDataProviderExecution are called twice in special setup (Krishnan Mahadevan)
45
Fixed: GITHUB-3038: java.lang.IllegalStateException: Results per method should NOT have been empty (Krishnan Mahadevan)
56
Fixed: GITHUB-3022: Remove deprecated JUnit related support in TestNG (Krishnan Mahadevan)
67

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.Collection;
44
import java.util.Collections;
5+
import java.util.Map;
6+
import org.testng.collections.Maps;
57
import org.testng.collections.Sets;
68

79
/**
@@ -10,11 +12,11 @@
1012
*/
1113
public class DataProviderHolder {
1214

13-
private final Collection<IDataProviderListener> listeners = Sets.newHashSet();
15+
private final Map<Class<?>, IDataProviderListener> listeners = Maps.newConcurrentMap();
1416
private final Collection<IDataProviderInterceptor> interceptors = Sets.newHashSet();
1517

1618
public Collection<IDataProviderListener> getListeners() {
17-
return Collections.unmodifiableCollection(listeners);
19+
return Collections.unmodifiableCollection(listeners.values());
1820
}
1921

2022
public Collection<IDataProviderInterceptor> getInterceptors() {
@@ -26,7 +28,7 @@ public void addListeners(Collection<IDataProviderListener> listeners) {
2628
}
2729

2830
public void addListener(IDataProviderListener listener) {
29-
listeners.add(listener);
31+
listeners.putIfAbsent(listener.getClass(), listener);
3032
}
3133

3234
public void addInterceptors(Collection<IDataProviderInterceptor> interceptors) {
@@ -38,7 +40,7 @@ public void addInterceptor(IDataProviderInterceptor interceptor) {
3840
}
3941

4042
public void merge(DataProviderHolder other) {
41-
this.listeners.addAll(other.getListeners());
43+
addListeners(other.getListeners());
4244
this.interceptors.addAll(other.getInterceptors());
4345
}
4446
}

testng-core/src/test/java/test/dataprovider/DataProviderTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.testng.internal.collections.Pair;
2323
import org.testng.internal.reflect.MethodMatcherException;
2424
import org.testng.xml.XmlClass;
25+
import org.testng.xml.XmlInclude;
26+
import org.testng.xml.XmlSuite;
2527
import org.testng.xml.XmlTest;
2628
import test.InvokedMethodNameListener;
2729
import test.SimpleBaseTest;
@@ -54,6 +56,9 @@
5456
import test.dataprovider.issue2934.TestCaseSample.CoreListener;
5557
import test.dataprovider.issue2934.TestCaseSample.ToggleDataProvider;
5658
import test.dataprovider.issue2980.LoggingListener;
59+
import test.dataprovider.issue3045.DataProviderListener;
60+
import test.dataprovider.issue3045.DataProviderTestClassSample;
61+
import test.dataprovider.issue3045.DataProviderWithoutListenerTestClassSample;
5762

5863
public class DataProviderTest extends SimpleBaseTest {
5964

@@ -666,6 +671,48 @@ public Object[][] getSuiteFileNames() {
666671
};
667672
}
668673

674+
@Test(description = "GITHUB-3045")
675+
public void testIfDataProviderListenerInvokedOnlyOncePerDataProvider() {
676+
runTest(DataProviderTestClassSample.class, "");
677+
}
678+
679+
@Test(description = "GITHUB-3045")
680+
public void testIfDataProviderListenerInvokedOnlyOncePerDataProviderWhenListenerAddedViaSuite() {
681+
runTest(DataProviderWithoutListenerTestClassSample.class, DataProviderListener.class.getName());
682+
}
683+
684+
private static void runTest(Class<?> clazz, String listener) {
685+
DataProviderListener.logs.clear();
686+
TestNG testng = new TestNG();
687+
XmlSuite xmlSuite = new XmlSuite();
688+
if (!listener.isEmpty()) {
689+
xmlSuite.addListener(DataProviderListener.class.getName());
690+
}
691+
xmlSuite.setName("suite1");
692+
xmlTest(xmlSuite, clazz, "Test1", "normalTest");
693+
xmlTest(xmlSuite, clazz, "Test2", "dataDrivenTest");
694+
testng.setXmlSuites(Collections.singletonList(xmlSuite));
695+
testng.setVerbose(2);
696+
testng.run();
697+
assertThat(DataProviderListener.logs).hasSize(2);
698+
assertThat(DataProviderListener.logs)
699+
.containsExactly(
700+
"[Test2]-beforeDataProviderExecution-dataProvider",
701+
"[Test2]-afterDataProviderExecution-dataProvider");
702+
}
703+
704+
private static void xmlTest(XmlSuite xmlSuite, Class<?> clazz, String name, String methodName) {
705+
XmlTest xmlTest = new XmlTest(xmlSuite);
706+
xmlTest.setName(name);
707+
xmlTest.setXmlClasses(List.of(xmlClass(clazz, methodName)));
708+
}
709+
710+
private static XmlClass xmlClass(Class<?> clazz, String methodName) {
711+
XmlClass xmlClass = new XmlClass(clazz);
712+
xmlClass.setIncludedMethods(List.of(new XmlInclude(methodName)));
713+
return xmlClass;
714+
}
715+
669716
private LoggingListener runDataProviderTest(boolean flag) {
670717
TestNG testng = new TestNG();
671718
testng.setTestClasses(new Class[] {test.dataprovider.issue2980.TestClassSample.class});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package test.dataprovider.issue3045;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import org.testng.IDataProviderListener;
6+
import org.testng.IDataProviderMethod;
7+
import org.testng.ITestContext;
8+
import org.testng.ITestNGMethod;
9+
10+
public class DataProviderListener implements IDataProviderListener {
11+
12+
public static List<String> logs = new ArrayList<>();
13+
14+
@Override
15+
public void beforeDataProviderExecution(
16+
IDataProviderMethod dataProviderMethod, ITestNGMethod method, ITestContext iTestContext) {
17+
logs.add(
18+
testName(iTestContext)
19+
+ "-beforeDataProviderExecution-"
20+
+ dataProviderMethod.getMethod().getName());
21+
}
22+
23+
@Override
24+
public void afterDataProviderExecution(
25+
IDataProviderMethod dataProviderMethod, ITestNGMethod method, ITestContext iTestContext) {
26+
logs.add(
27+
testName(iTestContext)
28+
+ "-afterDataProviderExecution-"
29+
+ dataProviderMethod.getMethod().getName());
30+
}
31+
32+
private static String testName(ITestContext ctx) {
33+
return "[" + ctx.getName() + "]";
34+
}
35+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package test.dataprovider.issue3045;
2+
3+
import org.testng.annotations.DataProvider;
4+
import org.testng.annotations.Listeners;
5+
import org.testng.annotations.Test;
6+
7+
@Listeners({DataProviderListener.class})
8+
public class DataProviderTestClassSample {
9+
10+
@DataProvider
11+
public Object[][] dataProvider() {
12+
return new Object[][] {{"Test_1"}, {"Test_2"}, {"Test_3"}};
13+
}
14+
15+
@Test(dataProvider = "dataProvider")
16+
public void dataDrivenTest(String ignored) {}
17+
18+
@Test
19+
public void normalTest() {}
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package test.dataprovider.issue3045;
2+
3+
import org.testng.annotations.DataProvider;
4+
import org.testng.annotations.Test;
5+
6+
public class DataProviderWithoutListenerTestClassSample {
7+
8+
@DataProvider
9+
public Object[][] dataProvider() {
10+
return new Object[][] {{"Test_1"}, {"Test_2"}, {"Test_3"}};
11+
}
12+
13+
@Test(dataProvider = "dataProvider")
14+
public void dataDrivenTest(String ignored) {}
15+
16+
@Test
17+
public void normalTest() {}
18+
}

0 commit comments

Comments
 (0)