org.testng.internal.MethodGroupsHelper 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
package org.testng.internal;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.testng.ITestClass;
import org.testng.ITestNGMethod;
import org.testng.annotations.IConfigurationAnnotation;
import org.testng.annotations.ITestOrConfiguration;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
import org.testng.internal.annotations.AnnotationHelper;
import org.testng.internal.annotations.IAnnotationFinder;
import org.testng.internal.collections.Pair;
/**
* Collections of helper methods to help deal with test methods
*
* @author Cedric Beust
* @author nullin
*/
public class MethodGroupsHelper {
private static final Map PATTERN_CACHE = new ConcurrentHashMap<>();
private static final Map, Boolean> MATCH_CACHE = new ConcurrentHashMap<>();
/**
* Collect all the methods that belong to the included groups and exclude all the methods that
* belong to an excluded group.
*/
static void collectMethodsByGroup(
ITestNGMethod[] methods,
boolean forTests,
List outIncludedMethods,
List outExcludedMethods,
RunInfo runInfo,
IAnnotationFinder finder,
boolean unique) {
for (ITestNGMethod tm : methods) {
boolean in = false;
Method m = tm.getConstructorOrMethod().getMethod();
//
// @Test method
//
if (forTests) {
in =
MethodGroupsHelper.includeMethod(
AnnotationHelper.findTest(finder, m),
runInfo,
tm,
forTests,
unique,
outIncludedMethods);
}
//
// @Configuration method
//
else {
IConfigurationAnnotation annotation = AnnotationHelper.findConfiguration(finder, m);
if (annotation.getAlwaysRun()) {
if (!unique || MethodGroupsHelper.isMethodAlreadyNotPresent(outIncludedMethods, tm)) {
in = true;
}
} else {
in =
MethodGroupsHelper.includeMethod(
AnnotationHelper.findTest(finder, tm),
runInfo,
tm,
forTests,
unique,
outIncludedMethods);
}
}
if (in) {
outIncludedMethods.add(tm);
} else {
outExcludedMethods.add(tm);
}
}
}
private static boolean includeMethod(
ITestOrConfiguration annotation,
RunInfo runInfo,
ITestNGMethod tm,
boolean forTests,
boolean unique,
List outIncludedMethods) {
boolean result = false;
if (MethodHelper.isEnabled(annotation)) {
if (runInfo.includeMethod(tm, forTests)) {
if (unique) {
if (MethodGroupsHelper.isMethodAlreadyNotPresent(outIncludedMethods, tm)) {
result = true;
}
} else {
result = true;
}
}
}
return result;
}
private static boolean isMethodAlreadyNotPresent(List result, ITestNGMethod tm) {
for (ITestNGMethod m : result) {
ConstructorOrMethod jm1 = m.getConstructorOrMethod();
ConstructorOrMethod jm2 = tm.getConstructorOrMethod();
if (jm1.getName().equals(jm2.getName())) {
// Same names, see if they are in the same hierarchy
Class c1 = jm1.getDeclaringClass();
Class c2 = jm2.getDeclaringClass();
if (c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1)) {
return false;
}
}
}
return true;
}
/** Extracts the map of groups and their corresponding methods from the classes
. */
public static Map> findGroupsMethods(
Collection classes, boolean before) {
Map> result = Maps.newHashMap();
for (ITestClass cls : classes) {
ITestNGMethod[] methods = before ? cls.getBeforeGroupsMethods() : cls.getAfterGroupsMethods();
for (ITestNGMethod method : methods) {
String[] grp = before ? method.getBeforeGroups() : method.getAfterGroups();
List groups = Stream.concat(Arrays.stream(grp), Arrays.stream(method.getGroups()))
.collect(Collectors.toList());
for (String group : groups) {
List methodList = result.computeIfAbsent(group, k -> Lists.newArrayList());
// NOTE(cbeust, 2007/01/23)
// BeforeGroups/AfterGroups methods should only be invoked once.
// I should probably use a map instead of a list for a contains(), but
// this list should usually be fairly short
if (!methodList.contains(method)) {
methodList.add(method);
}
}
}
}
return result;
}
protected static void findGroupTransitiveClosure(
List includedMethods,
List allMethods,
String[] includedGroups,
Set outGroups,
Set outMethods) {
Map runningMethods = includedMethods.stream().collect(Collectors.toMap(m -> m, m -> m));
Map runningGroups = Arrays.stream(includedGroups)
.collect(Collectors.toMap(g -> g, g -> g));
boolean keepGoing = true;
Map newMethods = Maps.newHashMap();
while (keepGoing) {
for (ITestNGMethod m : includedMethods) {
//
// Depends on groups?
// Adds all included methods to runningMethods
//
String[] ig = m.getGroupsDependedUpon();
for (String g : ig) {
if (!runningGroups.containsKey(g)) {
// Found a new included group, add all the methods it contains to
// our outMethod closure
runningGroups.put(g, g);
ITestNGMethod[] im =
MethodGroupsHelper.findMethodsThatBelongToGroup(
m, allMethods.toArray(new ITestNGMethod[0]), g);
for (ITestNGMethod thisMethod : im) {
if (!runningMethods.containsKey(thisMethod)) {
runningMethods.put(thisMethod, thisMethod);
newMethods.put(thisMethod, thisMethod);
}
}
}
} // groups
//
// Depends on methods?
// Adds all depended methods to runningMethods
//
String[] mdu = m.getMethodsDependedUpon();
for (String tm : mdu) {
ITestNGMethod thisMethod = MethodGroupsHelper.findMethodNamed(tm, allMethods);
if (thisMethod != null && !runningMethods.containsKey(thisMethod)) {
runningMethods.put(thisMethod, thisMethod);
newMethods.put(thisMethod, thisMethod);
}
}
} // methods
//
// Only keep going if new methods have been added
//
keepGoing = newMethods.size() > 0;
includedMethods = Lists.newArrayList();
includedMethods.addAll(newMethods.keySet());
newMethods = Maps.newHashMap();
} // while keepGoing
outMethods.addAll(runningMethods.keySet());
outGroups.addAll(runningGroups.keySet());
}
private static ITestNGMethod findMethodNamed(String tm, List allMethods) {
return allMethods.stream()
.filter(m -> m.getQualifiedName().equals(tm))
.findFirst()
.orElse(null);
}
/**
* Only used if a group is missing to flag an error on that method
*
* @param method if no group is found, group regex is set as this method's missing group
* @param methods list of methods to search
* @param groupRegexp regex representing the group
* @return all the methods that belong to the group specified by the regular expression
* groupRegExp. methods[] is the list of all the methods we are choosing from and method is
* the method that owns the dependsOnGroups statement (only used if a group is missing to flag
* an error on that method).
*/
protected static ITestNGMethod[] findMethodsThatBelongToGroup(
ITestNGMethod method, ITestNGMethod[] methods, String groupRegexp) {
ITestNGMethod[] found = findMethodsThatBelongToGroup(methods, groupRegexp);
if (found.length == 0) {
method.setMissingGroup(groupRegexp);
}
return found;
}
/**
* @param methods list of methods to search
* @param groupRegexp regex representing the group
* @return all the methods that belong to the group specified by the regular expression
* groupRegExp. methods[] is the list of all the methods we are choosing from.
*/
protected static ITestNGMethod[] findMethodsThatBelongToGroup(
ITestNGMethod[] methods, String groupRegexp) {
final Pattern pattern = getPattern(groupRegexp);
Predicate matchingGroups = tm -> Arrays.stream(tm.getGroups())
.anyMatch(group -> isMatch(pattern, group));
return Arrays.stream(methods)
.filter(matchingGroups)
.toArray(ITestNGMethod[]::new);
}
private static Boolean isMatch(Pattern pattern, String group) {
Pair cacheKey = Pair.create(pattern.pattern(), group);
Boolean match = MATCH_CACHE.get(cacheKey);
if (match == null) {
match = pattern.matcher(group).matches();
MATCH_CACHE.put(cacheKey, match);
}
return match;
}
private static Pattern getPattern(String groupRegexp) {
Pattern groupPattern = PATTERN_CACHE.get(groupRegexp);
if (groupPattern == null) {
groupPattern = Pattern.compile(groupRegexp);
PATTERN_CACHE.put(groupRegexp, groupPattern);
}
return groupPattern;
}
}