Skip to content

Commit

Permalink
Honour regex in dependsOnMethods
Browse files Browse the repository at this point in the history
Closes: #141
  • Loading branch information
krmahadevan committed Nov 29, 2022
1 parent 563bd6d commit 00de134
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
Current
Fixed: GITHUB-893: TestNG should provide an Api which allow to find all dependent of a specific test (Krishnan Mahadevan)
New: Added .yml file extension for yaml suite files, previously only .yaml was allowed for yaml (Steven Jubb)
Fixed: GITHUB-141: regular expression in "dependsOnMethods" does not work (Krishnan Mahadevan)
Fixed: GITHUB-2770: FileAlreadyExistsException when report is generated (melloware)
Fixed: GITHUB-2825: Programically Loading TestNG Suite from JAR File Fails to Delete Temporary Copy of Suite File (Steven Jubb)
Fixed: GITHUB-2825: Programmatically Loading TestNG Suite from JAR File Fails to Delete Temporary Copy of Suite File (Steven Jubb)
Fixed: GITHUB-2818: Add configuration key for callback discrepancy behavior (Krishnan Mahadevan)
Fixed: GITHUB-2819: Ability to retry a data provider in case of failures (Krishnan Mahadevan)
Fixed: GITHUB-2308: StringIndexOutOfBoundsException in findClassesInPackage - Surefire/Maven - JDK 11 fails (Krishnan Mahadevan)
Expand Down
10 changes: 10 additions & 0 deletions testng-core/src/main/java/org/testng/DependencyMap.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package org.testng;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.testng.collections.ListMultiMap;
import org.testng.collections.Maps;
import org.testng.internal.MethodHelper;
import org.testng.internal.RuntimeBehavior;

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

public ITestNGMethod getMethodDependingOn(String methodName, ITestNGMethod fromMethod) {
List<ITestNGMethod> l = m_dependencies.get(methodName);
if (l.isEmpty()) {
ITestNGMethod[] array =
m_dependencies.values().stream()
.flatMap(Collection::stream)
.toArray(ITestNGMethod[]::new);
l = Arrays.asList(MethodHelper.findDependedUponMethods(fromMethod, array));
}
if (l.isEmpty()) {
// Try to fetch dependencies by using the test class in the method name.
// This is usually needed in scenarios wherein a child class overrides a base class method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ private static Method findMethodByName(ITestNGMethod testngMethod, String regExp
if (regExp == null) {
return null;
}
regExp = regExp.replace("\\$", "$");
int lastDot = regExp.lastIndexOf('.');
String className, methodName;
if (lastDot == -1) {
Expand Down
90 changes: 89 additions & 1 deletion testng-core/src/test/java/test/dependent/DependentTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.TestNG;
import org.testng.TestNGException;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.xml.XmlSuite.ParallelMode;
Expand All @@ -22,14 +23,101 @@
import test.dependent.github1380.GitHub1380Sample2;
import test.dependent.github1380.GitHub1380Sample3;
import test.dependent.github1380.GitHub1380Sample4;
import test.dependent.issue141.ErrorScenarioNestedSample;
import test.dependent.issue141.MultipleMatchesTestClassSample;
import test.dependent.issue141.NestedTestClassSample;
import test.dependent.issue141.SimpleSample;
import test.dependent.issue141.SkipReasoner;
import test.dependent.issue141.TestClassSample;
import test.dependent.issue2658.FailingClassSample;
import test.dependent.issue2658.PassingClassSample;
import test.dependent.issue893.DependencyTrackingListener;
import test.dependent.issue893.MultiLevelDependenciesTestClassSample;
import test.dependent.issue893.TestClassSample;

public class DependentTest extends SimpleBaseTest {

@Test(description = "GITHUB-141")
public void ensureDependsOnMethodsHonoursRegexPatternsAcrossClasses() {
TestNG testng =
create(test.dependent.issue141.ASample.class, test.dependent.issue141.BSample.class);
MethodNameCollector listener = new MethodNameCollector();
testng.addListener(listener);
testng.run();
assertThat(listener.getPassedNames()).containsExactly("b", "bb", "a");
}

@Test(
description = "GITHUB-141",
expectedExceptions = TestNGException.class,
expectedExceptionsMessageRegExp =
"\ntest.dependent.issue141.SimpleSample.testMethod\\(\\) "
+ "depends on nonexistent method test.dependent.issue141.BSample.*")
public void ensureDependsOnMethodsHonoursRegexPatternsAcrossClassesErrorCondition() {
TestNG testng = create(SimpleSample.class, test.dependent.issue141.BSample.class);
MethodNameCollector listener = new MethodNameCollector();
testng.addListener(listener);
testng.run();
}

@Test(
description = "GITHUB-141",
expectedExceptions = TestNGException.class,
expectedExceptionsMessageRegExp =
"\ntest.dependent.issue141.ErrorScenarioNestedSample.a\\(\\) "
+ "depends on nonexistent "
+ "method test.dependent.issue141.ErrorScenarioNestedSample.*")
public void ensureDependsOnMethodsHonoursRegexPatternsNestedClassesErrorCondition() {
TestNG testng = create(ErrorScenarioNestedSample.class);
MethodNameCollector listener = new MethodNameCollector();
testng.addListener(listener);
testng.run();
}

@Test(description = "GITHUB-141")
public void ensureDependsOnMethodsHonoursRegexPatternsUniqueMatch() {
TestNG testng = create(TestClassSample.class);
MethodNameCollector listener = new MethodNameCollector();
testng.addListener(listener);
testng.run();
assertThat(listener.getPassedNames()).containsExactly("test_C6390323", "randomTest");
}

@Test(description = "GITHUB-141")
public void ensureDependsOnMethodsHonoursRegexPatternsDuplicateMatches() {
TestNG testng = create(MultipleMatchesTestClassSample.class);
MethodNameCollector listener = new MethodNameCollector();
SkipReasoner reasoner = new SkipReasoner();
testng.addListener(listener);
testng.addListener(reasoner);
testng.run();
assertThat(listener.getPassedNames()).containsExactly("test_C6390324");
assertThat(listener.getFailedNames()).containsExactly("test_C6390323");
assertThat(listener.getSkippedNames()).containsExactly("randomTest");
assertThat(reasoner.getUpstreamFailures()).containsExactly("test_C6390323");
}

@Test(description = "GITHUB-141")
public void ensureDependsOnMethodsHonoursRegexPatternsDuplicateMatchesNestedClasses() {
TestNG testng = create(NestedTestClassSample.class);
MethodNameCollector listener = new MethodNameCollector();
SkipReasoner reasoner = new SkipReasoner();
testng.addListener(listener);
testng.addListener(reasoner);
testng.run();
String clazzName =
NestedTestClassSample.class.getCanonicalName()
+ "$"
+ NestedTestClassSample.SecondSample.class.getSimpleName();
assertThat(listener.getFailedInstances()).hasSize(1);
assertThat(listener.getFailedInstances().get(0)).contains(clazzName);

assertThat(listener.getSkippedInstances()).hasSize(1);
assertThat(listener.getSkippedInstances().get(0)).contains(clazzName);

assertThat(listener.getFailedNames()).containsExactly("test_C6390323");
assertThat(listener.getSkippedNames()).containsExactly("randomTest");
}

@Test
public void simpleSkip() {
TestNG testng = create(SampleDependent1.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package test.dependent.issue141;

import org.testng.annotations.Test;

public class ASample {
@Test(dependsOnMethods = "test.dependent.issue141.BSample.b*")
public void a() {}
}
11 changes: 11 additions & 0 deletions testng-core/src/test/java/test/dependent/issue141/BSample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package test.dependent.issue141;

import org.testng.annotations.Test;

public class BSample {
@Test
public void b() {}

@Test
public void bb() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package test.dependent.issue141;

import org.testng.annotations.Test;

public class ErrorScenarioNestedSample {

@Test(
dependsOnMethods = "test.dependent.issue141.ErrorScenarioNestedSample$InnerTestClass.rambo*")
public void a() {}

public static class InnerTestClass {
@Test
public void b() {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package test.dependent.issue141;

import org.testng.Assert;
import org.testng.annotations.Test;

public class MultipleMatchesTestClassSample {

@Test(dependsOnMethods = "test_C[0-9]{7}")
public void randomTest() {}

@Test
public void test_C6390323() {
Assert.fail();
}

@Test
public void test_C6390324() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package test.dependent.issue141;

import org.testng.Assert;
import org.testng.annotations.Test;

public class NestedTestClassSample {

public static class FirstSample {
@Test(dependsOnMethods = "test_C[0-9]{7}")
public void randomTest() {}

@Test
public void test_C6390323() {}
}

public static class SecondSample {
@Test(dependsOnMethods = "test_C[0-9]{7}")
public void randomTest() {}

@Test
public void test_C6390323() {
Assert.fail();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package test.dependent.issue141;

import org.testng.annotations.Test;

public class SimpleSample {

@Test(dependsOnMethods = "test.dependent.issue141.BSample.xx*")
public void testMethod() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package test.dependent.issue141;

import java.util.List;
import java.util.stream.Collectors;
import org.testng.ITestListener;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;

public class SkipReasoner implements ITestListener {

private List<String> upstreamFailures;

@Override
public void onTestSkipped(ITestResult result) {
upstreamFailures =
result.getSkipCausedBy().stream()
.map(ITestNGMethod::getMethodName)
.collect(Collectors.toList());
}

public List<String> getUpstreamFailures() {
return upstreamFailures;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package test.dependent.issue141;

import org.testng.annotations.Test;

public class TestClassSample {

@Test(dependsOnMethods = "test_C[0-9]{7}")
public void randomTest() {}

@Test
public void test_C6390323() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.testng.IAttributes;
Expand Down Expand Up @@ -427,11 +428,24 @@ public List<ITestNGMethod> getSkipCausedBy() {
skippedDueTo =
allfailures.stream()
.map(ITestResult::getMethod)
.filter(method -> upstreamMethods.contains(method.getQualifiedName()))
.filter(method -> matches(upstreamMethods, method))
.collect(Collectors.toList());
return Collections.unmodifiableList(skippedDueTo);
}

private static boolean matches(List<String> upstreamMethods, ITestNGMethod method) {
if (upstreamMethods.contains(method.getQualifiedName())
|| upstreamMethods.contains(method.getMethodName())) {
return true;
}
return upstreamMethods.stream()
.map(Pattern::compile)
.anyMatch(
each ->
each.matcher(method.getQualifiedName()).matches()
|| each.matcher(method.getMethodName()).matches());
}

public String id() {
return id;
}
Expand Down

0 comments on commit 00de134

Please sign in to comment.