org.testng.internal.MethodHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of testng Show documentation
Show all versions of testng Show documentation
Testing framework for Java
The newest version!
package org.testng.internal;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.testng.IMethodInstance;
import org.testng.ITestClass;
import org.testng.ITestNGMethod;
import org.testng.TestNGException;
import org.testng.annotations.IConfigurationAnnotation;
import org.testng.annotations.ITestAnnotation;
import org.testng.annotations.ITestOrConfiguration;
import org.testng.collections.Lists;
import org.testng.collections.Sets;
import org.testng.internal.annotations.AnnotationHelper;
import org.testng.internal.annotations.IAnnotationFinder;
import org.testng.internal.collections.Pair;
import org.testng.internal.invokers.IInvocationStatus;
import org.testng.util.TimeUtils;
import org.testng.xml.XmlTest;
/** Collection of helper methods to help sort and arrange methods. */
public class MethodHelper {
private static final Map> GRAPH_CACHE =
new ConcurrentHashMap<>();
private static final Map CANONICAL_NAME_CACHE = new ConcurrentHashMap<>();
private static final Map, Boolean> MATCH_CACHE = new ConcurrentHashMap<>();
/**
* Collects and orders test or configuration methods
*
* @param methods methods to be worked on
* @param forTests true for test methods, false for configuration methods
* @param runInfo - {@link RunInfo} object.
* @param finder annotation finder
* @param unique true for unique methods, false otherwise
* @param outExcludedMethods - A List of excluded {@link ITestNGMethod} methods.
* @return an array of ordered methods
*/
public static ITestNGMethod[] collectAndOrderMethods(
List methods,
boolean forTests,
RunInfo runInfo,
IAnnotationFinder finder,
boolean unique,
List outExcludedMethods,
Comparator comparator) {
AtomicReference results = new AtomicReference<>();
List includedMethods = Lists.newArrayList();
TimeUtils.computeAndShowTime(
"MethodGroupsHelper.collectMethodsByGroup()",
() ->
MethodGroupsHelper.collectMethodsByGroup(
methods.toArray(new ITestNGMethod[0]),
forTests,
includedMethods,
outExcludedMethods,
runInfo,
finder,
unique));
TimeUtils.computeAndShowTime(
"MethodGroupsHelper.sortMethods()",
() ->
results.set(
sortMethods(forTests, includedMethods, comparator)
.toArray(new ITestNGMethod[] {})));
return results.get();
}
/**
* Finds TestNG methods that the specified TestNG method depends upon
*
* @param m TestNG method
* @param methods list of methods to search for depended upon methods
* @return an array of methods that match the criteria
*/
protected static ITestNGMethod[] findDependedUponMethods(
ITestNGMethod m, List methods) {
ITestNGMethod[] methodsArray = methods.toArray(new ITestNGMethod[0]);
return findDependedUponMethods(m, methodsArray);
}
private static Pair> filterToUse(ITestNGMethod m) {
if (m.isBeforeMethodConfiguration()) {
return new Pair<>("BeforeMethod", ITestNGMethod::isBeforeMethodConfiguration);
}
if (m.isAfterMethodConfiguration()) {
return new Pair<>("AfterMethod", ITestNGMethod::isAfterMethodConfiguration);
}
if (m.isBeforeClassConfiguration()) {
return new Pair<>("BeforeClass", ITestNGMethod::isBeforeClassConfiguration);
}
if (m.isAfterClassConfiguration()) {
return new Pair<>("AfterClass", ITestNGMethod::isAfterClassConfiguration);
}
if (m.isBeforeTestConfiguration()) {
return new Pair<>("BeforeTest", ITestNGMethod::isBeforeTestConfiguration);
}
if (m.isAfterTestConfiguration()) {
return new Pair<>("AfterTest", ITestNGMethod::isAfterTestConfiguration);
}
if (m.isBeforeSuiteConfiguration()) {
return new Pair<>("BeforeSuite", ITestNGMethod::isBeforeSuiteConfiguration);
}
if (m.isAfterSuiteConfiguration()) {
return new Pair<>("AfterSuite", ITestNGMethod::isAfterSuiteConfiguration);
}
if (m.isBeforeGroupsConfiguration()) {
return new Pair<>("BeforeGroups", ITestNGMethod::isBeforeGroupsConfiguration);
}
if (m.isAfterGroupsConfiguration()) {
return new Pair<>("AfterGroups", ITestNGMethod::isAfterGroupsConfiguration);
}
return new Pair<>("Test", ITestNGMethod::isTest);
}
/**
* Finds TestNG methods that the specified TestNG method depends upon
*
* @param m TestNG method
* @param incoming list of methods to search for depended upon methods
* @return an array of methods that match the criteria
*/
public static ITestNGMethod[] findDependedUponMethods(ITestNGMethod m, ITestNGMethod[] incoming) {
ITestNGMethod[] methods =
Arrays.stream(incoming)
.filter(each -> !each.equals(m))
.filter(each -> Objects.isNull(each.getRealClass().getEnclosingClass()))
.toArray(ITestNGMethod[]::new);
String canonicalMethodName = calculateMethodCanonicalName(m);
Pair> filterPair = filterToUse(m);
String annotationType = filterPair.first();
Predicate predicate = filterPair.second();
if (isConfigurationMethod(m)) {
methods =
Arrays.stream(incoming)
.filter(tm -> !tm.equals(m)) // exclude the current config method from the list
.filter(predicate) // include only similar config methods
.toArray(ITestNGMethod[]::new);
if (methods.length == 0) {
String msg =
String.format(
"None of the dependencies of the method %s are annotated with [@%s].",
canonicalMethodName, annotationType);
throw new TestNGException(msg);
}
}
List vResult = Lists.newArrayList();
String regexp = null;
for (String fullyQualifiedRegexp : m.getMethodsDependedUpon()) {
boolean foundAtLeastAMethod = false;
if (null != fullyQualifiedRegexp) {
// Escapes $ in regexps as it is not meant for end - line matching, but inner class matches.
regexp = fullyQualifiedRegexp.replace("$", "\\$");
MatchResults results = matchMethod(methods, regexp);
foundAtLeastAMethod = results.foundAtLeastAMethod;
vResult.addAll(results.matchedMethods);
if (!foundAtLeastAMethod) {
// Replace the declaring class name in the dependsOnMethods value with
// the fully qualified test class name and retry the method matching.
int lastIndex = regexp.lastIndexOf('.');
String newMethodName;
if (lastIndex != -1) {
String clazzName =
(m.getTestClass() != null ? m.getTestClass().getRealClass() : m.getRealClass())
.getName();
newMethodName = clazzName + regexp.substring(lastIndex);
results = matchMethod(methods, newMethodName);
foundAtLeastAMethod = results.foundAtLeastAMethod;
vResult.addAll(results.matchedMethods);
}
}
}
if (!foundAtLeastAMethod) {
if (m.ignoreMissingDependencies()) {
continue;
}
if (m.isAlwaysRun()) {
continue;
}
Method maybeReferringTo = findMethodByName(m, regexp);
if (maybeReferringTo != null) {
String suffix = " or not included.";
if (isConfigurationMethod(m)) {
suffix = ".";
}
throw new TestNGException(
canonicalMethodName
+ "() is depending on method "
+ maybeReferringTo
+ ", which is not annotated with @"
+ annotationType
+ suffix);
}
throw new TestNGException(
canonicalMethodName + "() depends on nonexistent method " + regexp);
}
} // end for
return vResult.toArray(new ITestNGMethod[0]);
}
/**
* Finds method based on regex and TestNGMethod. If regex doesn't represent the class name, uses
* the TestNG method's class name.
*
* @param testngMethod TestNG method
* @param regExp regex representing a method and/or related class name
*/
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) {
className = testngMethod.getConstructorOrMethod().getDeclaringClass().getCanonicalName();
methodName = regExp;
} else {
methodName = regExp.substring(lastDot + 1);
className = regExp.substring(0, lastDot);
}
try {
Class> c = Class.forName(className);
for (Method m : c.getDeclaredMethods()) {
if (methodName.equals(m.getName())) {
return m;
}
}
} catch (Exception e) {
// only logging
Utils.log("MethodHelper", 3, "Caught exception while searching for methods using regex");
}
return null;
}
public static boolean isEnabled(Class> objectClass, IAnnotationFinder finder) {
ITestAnnotation testClassAnnotation = AnnotationHelper.findTest(finder, objectClass);
return isEnabled(testClassAnnotation);
}
public static boolean isEnabled(Method m, IAnnotationFinder finder) {
ITestAnnotation annotation = AnnotationHelper.findTest(finder, m);
// If no method annotation, look for one on the class
if (null == annotation) {
annotation = AnnotationHelper.findTest(finder, m.getDeclaringClass());
}
return isEnabled(annotation);
}
public static boolean isEnabled(ITestOrConfiguration test) {
return null == test || test.getEnabled();
}
public static boolean isAlwaysRun(IConfigurationAnnotation configurationAnnotation) {
if (null == configurationAnnotation) {
return false;
}
boolean alwaysRun = false;
if ((configurationAnnotation.getAfterSuite()
|| configurationAnnotation.getAfterTest()
|| configurationAnnotation.getAfterTestClass()
|| configurationAnnotation.getAfterTestMethod()
|| configurationAnnotation.getBeforeTestMethod()
|| configurationAnnotation.getBeforeTestClass()
|| configurationAnnotation.getBeforeTest()
|| configurationAnnotation.getBeforeSuite()
|| configurationAnnotation.getBeforeGroups().length != 0
|| configurationAnnotation.getAfterGroups().length != 0)
&& configurationAnnotation.getAlwaysRun()) {
alwaysRun = true;
}
return alwaysRun;
}
/** Extracts the unique list of ITestNGMethod
s. */
public static List uniqueMethodList(Collection> methods) {
Set resultSet = Sets.newHashSet();
for (List l : methods) {
resultSet.addAll(l);
}
return Lists.newArrayList(resultSet);
}
private static Graph topologicalSort(
ITestNGMethod[] methods,
List sequentialList,
List parallelList,
final Comparator comparator) {
Graph result =
new Graph<>((o1, o2) -> comparator.compare(o1.getObject(), o2.getObject()));
if (methods.length == 0) {
return result;
}
//
// Create the graph
//
Map
© 2015 - 2024 Weber Informatics LLC | Privacy Policy