Skip to content

Commit a5b8508

Browse files
committed
Honour regex in dependsOnMethods
Closes: #141
1 parent 563bd6d commit a5b8508

File tree

14 files changed

+285
-8
lines changed

14 files changed

+285
-8
lines changed

CHANGES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
Current
22
Fixed: GITHUB-893: TestNG should provide an Api which allow to find all dependent of a specific test (Krishnan Mahadevan)
33
New: Added .yml file extension for yaml suite files, previously only .yaml was allowed for yaml (Steven Jubb)
4+
Fixed: GITHUB-141: regular expression in "dependsOnMethods" does not work (Krishnan Mahadevan)
45
Fixed: GITHUB-2770: FileAlreadyExistsException when report is generated (melloware)
5-
Fixed: GITHUB-2825: Programically Loading TestNG Suite from JAR File Fails to Delete Temporary Copy of Suite File (Steven Jubb)
6+
Fixed: GITHUB-2825: Programmatically Loading TestNG Suite from JAR File Fails to Delete Temporary Copy of Suite File (Steven Jubb)
67
Fixed: GITHUB-2818: Add configuration key for callback discrepancy behavior (Krishnan Mahadevan)
78
Fixed: GITHUB-2819: Ability to retry a data provider in case of failures (Krishnan Mahadevan)
89
Fixed: GITHUB-2308: StringIndexOutOfBoundsException in findClassesInPackage - Surefire/Maven - JDK 11 fails (Krishnan Mahadevan)

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package org.testng;
22

3+
import java.util.Arrays;
4+
import java.util.Collection;
35
import java.util.List;
46
import java.util.Optional;
57
import java.util.Set;
68
import java.util.regex.Pattern;
79
import java.util.stream.Collectors;
810
import org.testng.collections.ListMultiMap;
911
import org.testng.collections.Maps;
12+
import org.testng.internal.MethodHelper;
1013
import org.testng.internal.RuntimeBehavior;
1114

1215
/** Helper class to keep track of dependencies. */
@@ -54,6 +57,13 @@ public List<ITestNGMethod> getMethodsThatBelongTo(String group, ITestNGMethod fr
5457

5558
public ITestNGMethod getMethodDependingOn(String methodName, ITestNGMethod fromMethod) {
5659
List<ITestNGMethod> l = m_dependencies.get(methodName);
60+
if (l.isEmpty()) {
61+
ITestNGMethod[] array =
62+
m_dependencies.values().stream()
63+
.flatMap(Collection::stream)
64+
.toArray(ITestNGMethod[]::new);
65+
l = Arrays.asList(MethodHelper.findDependedUponMethods(fromMethod, array));
66+
}
5767
if (l.isEmpty()) {
5868
// Try to fetch dependencies by using the test class in the method name.
5969
// This is usually needed in scenarios wherein a child class overrides a base class method.
@@ -78,7 +88,11 @@ public ITestNGMethod getMethodDependingOn(String methodName, ITestNGMethod fromM
7888
}
7989

8090
throw new TestNGException(
81-
"Method \"" + fromMethod + "\" depends on nonexistent method \"" + methodName + "\"");
91+
"Method \""
92+
+ fromMethod.getQualifiedName()
93+
+ "()\" depends on nonexistent method \""
94+
+ methodName
95+
+ "\"");
8296
}
8397

8498
private static boolean belongToDifferentClassHierarchy(

testng-core/src/main/java/org/testng/internal/MethodHelper.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,18 @@ protected static ITestNGMethod[] findDependedUponMethods(
9494
* Finds TestNG methods that the specified TestNG method depends upon
9595
*
9696
* @param m TestNG method
97-
* @param methods list of methods to search for depended upon methods
97+
* @param incoming list of methods to search for depended upon methods
9898
* @return list of methods that match the criteria
9999
*/
100-
public static ITestNGMethod[] findDependedUponMethods(ITestNGMethod m, ITestNGMethod[] methods) {
100+
public static ITestNGMethod[] findDependedUponMethods(ITestNGMethod m, ITestNGMethod[] incoming) {
101+
ITestNGMethod[] methods =
102+
Arrays.stream(incoming)
103+
.filter(each -> !each.equals(m))
104+
.filter(each -> Objects.isNull(each.getRealClass().getEnclosingClass()))
105+
.toArray(ITestNGMethod[]::new);
106+
if (methods.length == 0) {
107+
return new ITestNGMethod[] {};
108+
}
101109

102110
String canonicalMethodName = calculateMethodCanonicalName(m);
103111

@@ -163,6 +171,7 @@ private static Method findMethodByName(ITestNGMethod testngMethod, String regExp
163171
if (regExp == null) {
164172
return null;
165173
}
174+
regExp = regExp.replace("\\$", "$");
166175
int lastDot = regExp.lastIndexOf('.');
167176
String className, methodName;
168177
if (lastDot == -1) {

testng-core/src/test/java/test/dependent/DependentTest.java

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.testng.ITestListener;
1212
import org.testng.ITestResult;
1313
import org.testng.TestNG;
14+
import org.testng.TestNGException;
1415
import org.testng.annotations.DataProvider;
1516
import org.testng.annotations.Test;
1617
import org.testng.xml.XmlSuite.ParallelMode;
@@ -22,14 +23,111 @@
2223
import test.dependent.github1380.GitHub1380Sample2;
2324
import test.dependent.github1380.GitHub1380Sample3;
2425
import test.dependent.github1380.GitHub1380Sample4;
26+
import test.dependent.issue141.ErrorScenarioNestedSample;
27+
import test.dependent.issue141.MultipleMatchesTestClassSample;
28+
import test.dependent.issue141.NestedTestClassSample;
29+
import test.dependent.issue141.NestedTestClassSample2;
30+
import test.dependent.issue141.SimpleSample;
31+
import test.dependent.issue141.SkipReasoner;
32+
import test.dependent.issue141.TestClassSample;
2533
import test.dependent.issue2658.FailingClassSample;
2634
import test.dependent.issue2658.PassingClassSample;
2735
import test.dependent.issue893.DependencyTrackingListener;
2836
import test.dependent.issue893.MultiLevelDependenciesTestClassSample;
29-
import test.dependent.issue893.TestClassSample;
3037

3138
public class DependentTest extends SimpleBaseTest {
3239

40+
@Test(description = "GITHUB-141")
41+
public void ensureDependsOnMethodsHonoursRegexPatternsAcrossClasses() {
42+
TestNG testng =
43+
create(test.dependent.issue141.ASample.class, test.dependent.issue141.BSample.class);
44+
MethodNameCollector listener = new MethodNameCollector();
45+
testng.addListener(listener);
46+
testng.run();
47+
assertThat(listener.getPassedNames()).containsExactly("b", "bb", "a");
48+
}
49+
50+
@Test(
51+
description = "GITHUB-141",
52+
expectedExceptions = TestNGException.class,
53+
expectedExceptionsMessageRegExp =
54+
"\ntest.dependent.issue141.SimpleSample.testMethod\\(\\) "
55+
+ "depends on nonexistent method test.dependent.issue141.BSample.*")
56+
public void ensureDependsOnMethodsHonoursRegexPatternsAcrossClassesErrorCondition() {
57+
TestNG testng = create(SimpleSample.class, test.dependent.issue141.BSample.class);
58+
MethodNameCollector listener = new MethodNameCollector();
59+
testng.addListener(listener);
60+
testng.run();
61+
}
62+
63+
@Test(
64+
description = "GITHUB-141",
65+
expectedExceptions = TestNGException.class,
66+
expectedExceptionsMessageRegExp =
67+
"\nMethod \"test.dependent.issue141.ErrorScenarioNestedSample.a\\(\\)\" "
68+
+ "depends on nonexistent "
69+
+ "method \"test.dependent.issue141.ErrorScenarioNestedSample\\$InnerTestClass"
70+
+ ".rambo.*")
71+
public void ensureDependsOnMethodsHonoursRegexPatternsNestedClassesErrorCondition() {
72+
TestNG testng = create(ErrorScenarioNestedSample.class);
73+
MethodNameCollector listener = new MethodNameCollector();
74+
testng.addListener(listener);
75+
testng.run();
76+
}
77+
78+
@Test(description = "GITHUB-141")
79+
public void ensureDependsOnMethodsHonoursRegexPatternsUniqueMatch() {
80+
TestNG testng = create(TestClassSample.class);
81+
MethodNameCollector listener = new MethodNameCollector();
82+
testng.addListener(listener);
83+
testng.run();
84+
assertThat(listener.getPassedNames()).containsExactly("test_C6390323", "randomTest");
85+
}
86+
87+
@Test(description = "GITHUB-141")
88+
public void ensureDependsOnMethodsHonoursRegexPatternsDuplicateMatches() {
89+
TestNG testng = create(MultipleMatchesTestClassSample.class);
90+
MethodNameCollector listener = new MethodNameCollector();
91+
SkipReasoner reasoner = new SkipReasoner();
92+
testng.addListener(listener);
93+
testng.addListener(reasoner);
94+
testng.run();
95+
assertThat(listener.getPassedNames()).containsExactly("test_C6390324");
96+
assertThat(listener.getFailedNames()).containsExactly("test_C6390323");
97+
assertThat(listener.getSkippedNames()).containsExactly("randomTest");
98+
assertThat(reasoner.getUpstreamFailures()).containsExactly("test_C6390323");
99+
}
100+
101+
@Test(
102+
description = "GITHUB-141",
103+
expectedExceptions = TestNGException.class,
104+
expectedExceptionsMessageRegExp =
105+
"\nMethod \"test.dependent.issue141.NestedTestClassSample\\$FirstSample.randomTest\\(\\)\" "
106+
+ "depends on nonexistent method .*")
107+
public void ensureDependsOnMethodsHonoursRegexPatternsDuplicateMatchesNestedClasses() {
108+
TestNG testng = create(NestedTestClassSample.class);
109+
MethodNameCollector listener = new MethodNameCollector();
110+
SkipReasoner reasoner = new SkipReasoner();
111+
testng.addListener(listener);
112+
testng.addListener(reasoner);
113+
testng.run();
114+
}
115+
116+
@Test(
117+
description = "GITHUB-141",
118+
expectedExceptions = TestNGException.class,
119+
expectedExceptionsMessageRegExp =
120+
"\nMethod \"test.dependent.issue141.NestedTestClassSample2.randomTest\\(\\)\" depends on "
121+
+ "nonexistent method .*")
122+
public void ensureDependsOnMethodsHonourRegexPatternsNestedClasses() {
123+
TestNG testng = create(NestedTestClassSample2.class);
124+
MethodNameCollector listener = new MethodNameCollector();
125+
SkipReasoner reasoner = new SkipReasoner();
126+
testng.addListener(listener);
127+
testng.addListener(reasoner);
128+
testng.run();
129+
}
130+
33131
@Test
34132
public void simpleSkip() {
35133
TestNG testng = create(SampleDependent1.class);
@@ -241,7 +339,7 @@ public void testDownstreamDependencyRetrieval(
241339
public Object[][] getTestData() {
242340
return new Object[][] {
243341
{
244-
TestClassSample.class,
342+
test.dependent.issue893.TestClassSample.class,
245343
"independentTest",
246344
new String[] {"anotherDependentTest", "dependentTest"}
247345
},
@@ -273,7 +371,11 @@ public void testUpstreamDependencyRetrieval(
273371
@DataProvider(name = "getUpstreamTestData")
274372
public Object[][] getUpstreamTestData() {
275373
return new Object[][] {
276-
{TestClassSample.class, "dependentTest", new String[] {"independentTest"}},
374+
{
375+
test.dependent.issue893.TestClassSample.class,
376+
"dependentTest",
377+
new String[] {"independentTest"}
378+
},
277379
{MultiLevelDependenciesTestClassSample.class, "father", new String[] {"grandFather"}},
278380
{MultiLevelDependenciesTestClassSample.class, "child", new String[] {"father", "mother"}},
279381
{MultiLevelDependenciesTestClassSample.class, "grandFather", new String[] {}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class ASample {
6+
@Test(dependsOnMethods = "test.dependent.issue141.BSample.b*")
7+
public void a() {}
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class BSample {
6+
@Test
7+
public void b() {}
8+
9+
@Test
10+
public void bb() {}
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class ErrorScenarioNestedSample {
6+
7+
@Test(
8+
dependsOnMethods = "test.dependent.issue141.ErrorScenarioNestedSample$InnerTestClass.rambo*")
9+
public void a() {}
10+
11+
public static class InnerTestClass {
12+
@Test
13+
public void b() {}
14+
}
15+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.Assert;
4+
import org.testng.annotations.Test;
5+
6+
public class MultipleMatchesTestClassSample {
7+
8+
@Test(dependsOnMethods = "test_C[0-9]{7}")
9+
public void randomTest() {}
10+
11+
@Test
12+
public void test_C6390323() {
13+
Assert.fail();
14+
}
15+
16+
@Test
17+
public void test_C6390324() {}
18+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.Assert;
4+
import org.testng.annotations.Test;
5+
6+
public class NestedTestClassSample {
7+
8+
public static class FirstSample {
9+
@Test(dependsOnMethods = "test_C[0-9]{7}")
10+
public void randomTest() {}
11+
12+
@Test
13+
public void test_C6390323() {}
14+
}
15+
16+
public static class SecondSample {
17+
@Test(dependsOnMethods = "test_C[0-9]{7}")
18+
public void randomTest() {}
19+
20+
@Test
21+
public void test_C6390323() {
22+
Assert.fail();
23+
}
24+
}
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class NestedTestClassSample2 {
6+
7+
@Test(dependsOnMethods = "test_C[0-9]{7}")
8+
public void randomTest() {}
9+
10+
public static class InnerClass {
11+
12+
@Test
13+
public void test_C6390323() {}
14+
}
15+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class SimpleSample {
6+
7+
@Test(dependsOnMethods = "test.dependent.issue141.BSample.xx*")
8+
public void testMethod() {}
9+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package test.dependent.issue141;
2+
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
import org.testng.ITestListener;
6+
import org.testng.ITestNGMethod;
7+
import org.testng.ITestResult;
8+
9+
public class SkipReasoner implements ITestListener {
10+
11+
private List<String> upstreamFailures;
12+
13+
@Override
14+
public void onTestSkipped(ITestResult result) {
15+
upstreamFailures =
16+
result.getSkipCausedBy().stream()
17+
.map(ITestNGMethod::getMethodName)
18+
.collect(Collectors.toList());
19+
}
20+
21+
public List<String> getUpstreamFailures() {
22+
return upstreamFailures;
23+
}
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package test.dependent.issue141;
2+
3+
import org.testng.annotations.Test;
4+
5+
public class TestClassSample {
6+
7+
@Test(dependsOnMethods = "test_C[0-9]{7}")
8+
public void randomTest() {}
9+
10+
@Test
11+
public void test_C6390323() {}
12+
}

0 commit comments

Comments
 (0)