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;
public class MethodGroupsHelper {
private static final Map<String, Pattern> PATTERN_CACHE = new ConcurrentHashMap<>();
private static final Map<Pair<String, String>, Boolean> MATCH_CACHE = new ConcurrentHashMap<>();
static void collectMethodsByGroup(
ITestNGMethod[] methods,
boolean forTests,
List<ITestNGMethod> outIncludedMethods,
List<ITestNGMethod> outExcludedMethods,
RunInfo runInfo,
IAnnotationFinder finder,
boolean unique) {
for (ITestNGMethod tm : methods) {
boolean in = false;
Method m = tm.getConstructorOrMethod().getMethod();
if (forTests) {
in =
MethodGroupsHelper.includeMethod(
AnnotationHelper.findTest(finder, m),
runInfo,
tm,
forTests,
unique,
outIncludedMethods);
}
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<ITestNGMethod> 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<ITestNGMethod> result, ITestNGMethod tm) {
for (ITestNGMethod m : result) {
ConstructorOrMethod jm1 = m.getConstructorOrMethod();
ConstructorOrMethod jm2 = tm.getConstructorOrMethod();
if (jm1.getName().equals(jm2.getName())) {
Class<?> c1 = jm1.getDeclaringClass();
Class<?> c2 = jm2.getDeclaringClass();
if (c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1)) {
return false;
}
}
}
return true;
}
public static Map<String, List<ITestNGMethod>> findGroupsMethods(
Collection<ITestClass> classes, boolean before) {
Map<String, List<ITestNGMethod>> 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<String> groups = Stream.concat(Arrays.stream(grp), Arrays.stream(method.getGroups()))
.collect(Collectors.toList());
for (String group : groups) {
List<ITestNGMethod> methodList = result.computeIfAbsent(group, k -> Lists.newArrayList());
if (!methodList.contains(method)) {
methodList.add(method);
}
}
}
}
return result;
}
protected static void findGroupTransitiveClosure(
List<ITestNGMethod> includedMethods,
List<ITestNGMethod> allMethods,
String[] includedGroups,
Set<String> outGroups,
Set<ITestNGMethod> outMethods) {
Map<ITestNGMethod, ITestNGMethod> runningMethods = includedMethods.stream().collect(Collectors.toMap(m -> m, m -> m));
Map<String, String> runningGroups = Arrays.stream(includedGroups)
.collect(Collectors.toMap(g -> g, g -> g));
boolean keepGoing = true;
Map<ITestNGMethod, ITestNGMethod> newMethods = Maps.newHashMap();
while (keepGoing) {
for (ITestNGMethod m : includedMethods) {
String[] ig = m.getGroupsDependedUpon();
for (String g : ig) {
if (!runningGroups.containsKey(g)) {
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);
}
}
}
}
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);
}
}
}
keepGoing = newMethods.size() > 0;
includedMethods = Lists.newArrayList();
includedMethods.addAll(newMethods.keySet());
newMethods = Maps.newHashMap();
}
outMethods.addAll(runningMethods.keySet());
outGroups.addAll(runningGroups.keySet());
}
private static ITestNGMethod findMethodNamed(String tm, List<ITestNGMethod> allMethods) {
return allMethods.stream()
.filter(m -> m.getQualifiedName().equals(tm))
.findFirst()
.orElse(null);
}
protected static ITestNGMethod[] findMethodsThatBelongToGroup(
ITestNGMethod method, ITestNGMethod[] methods, String groupRegexp) {
ITestNGMethod[] found = findMethodsThatBelongToGroup(methods, groupRegexp);
if (found.length == 0) {
method.setMissingGroup(groupRegexp);
}
return found;
}
protected static ITestNGMethod[] findMethodsThatBelongToGroup(
ITestNGMethod[] methods, String groupRegexp) {
final Pattern pattern = getPattern(groupRegexp);
Predicate<ITestNGMethod> 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<String, String> 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;
}
}