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
/** * Collections of helper methods to help deal with test methods */
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<>();
Collect all the methods that belong to the included groups and exclude all the methods that belong to an excluded group.
/** * 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<ITestNGMethod> outIncludedMethods, List<ITestNGMethod> 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<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())) { // 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; }
Returns:the map of groups and their corresponding methods from the extraction of classes.
/** @return the map of groups and their corresponding methods from the extraction of <code>classes</code>. */
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()); // 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<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) { // // 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<ITestNGMethod> 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
Params:
  • method – if no group is found, group regex is set as this method's missing group
  • methods – list of methods to search
  • groupRegexp – regex representing the group
Returns: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).
/** * 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; }
Params:
  • methods – list of methods to search
  • groupRegexp – regex representing the group
Returns: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.
/** * @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<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; } }