• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.testng.internal;
2 
3 import java.lang.reflect.Method;
4 import java.util.Collection;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.regex.Pattern;
10 
11 import org.testng.IMethodSelector;
12 import org.testng.IMethodSelectorContext;
13 import org.testng.ITestNGMethod;
14 import org.testng.TestNGException;
15 import org.testng.collections.ListMultiMap;
16 import org.testng.collections.Lists;
17 import org.testng.collections.Maps;
18 import org.testng.xml.XmlClass;
19 import org.testng.xml.XmlInclude;
20 
21 /**
22  * This class is the default method selector used by TestNG to determine
23  * which methods need to be included and excluded based on the specification
24  * given in testng.xml.
25  *
26  * Created on Sep 30, 2005
27  * @author cbeust
28  */
29 public class XmlMethodSelector implements IMethodSelector {
30   private static final long serialVersionUID = -9030548178025605629L;
31 
32   // Groups included and excluded for this run
33   private Map<String, String> m_includedGroups = Maps.newHashMap();
34   private Map<String, String> m_excludedGroups = Maps.newHashMap();
35   private List<XmlClass> m_classes = null;
36   // The BeanShell expression for this test, if any
37   private String m_expression = null;
38   // List of methods included implicitly
39   private ListMultiMap<String, XmlInclude> m_includedMethods = Maps.newListMultiMap();
40   private IBsh m_bsh = Dynamic.hasBsh() ? new Bsh() : new BshMock();
41 
42   @Override
includeMethod(IMethodSelectorContext context, ITestNGMethod tm, boolean isTestMethod)43   public boolean includeMethod(IMethodSelectorContext context,
44       ITestNGMethod tm, boolean isTestMethod)
45   {
46 //    ppp("XML METHOD SELECTOR " + tm + " " + m_isInitialized);
47 
48     if (! m_isInitialized) {
49       m_isInitialized = true;
50       init(context);
51     }
52 
53     boolean result = false;
54     if (null != m_expression) {
55       return m_bsh.includeMethodFromExpression(m_expression, tm);
56     }
57     else {
58       result = includeMethodFromIncludeExclude(tm, isTestMethod);
59     }
60 
61     return result;
62   }
63 
includeMethodFromIncludeExclude(ITestNGMethod tm, boolean isTestMethod)64   private boolean includeMethodFromIncludeExclude(ITestNGMethod tm, boolean isTestMethod) {
65     boolean result = false;
66     Method m = tm.getMethod();
67     String[] groups = tm.getGroups();
68     Map<String, String> includedGroups = m_includedGroups;
69     Map<String, String> excludedGroups = m_excludedGroups;
70     List<XmlInclude> includeList =
71         m_includedMethods.get(MethodHelper.calculateMethodCanonicalName(tm));
72 
73     //
74     // No groups were specified:
75     //
76     if (includedGroups.size() == 0 && excludedGroups.size() == 0
77         && ! hasIncludedMethods() && ! hasExcludedMethods())
78     //
79     // If we don't include or exclude any methods, method is in
80     //
81     {
82       result = true;
83     }
84     //
85     // If it's a configuration method and no groups were requested, we want it in
86     //
87     else if (includedGroups.size() == 0 && excludedGroups.size() == 0 && ! isTestMethod)
88     {
89       result = true;
90     }
91 
92     //
93     // Is this method included implicitly?
94     //
95     else if (includeList != null) {
96       result = true;
97     }
98 
99     //
100     // Include or Exclude groups were specified:
101     //
102     else {
103       //
104       // Only add this method if it belongs to an included group and not
105       // to an excluded group
106       //
107       {
108         boolean isIncludedInGroups = isIncluded(groups, m_includedGroups.values());
109         boolean isExcludedInGroups = isExcluded(groups, m_excludedGroups.values());
110 
111         //
112         // Calculate the run methods by groups first
113         //
114         if (isIncludedInGroups && !isExcludedInGroups) {
115           result = true;
116         }
117         else if (isExcludedInGroups) {
118           result = false;
119         }
120       }
121 
122       if(isTestMethod) {
123         //
124         // Now filter by method name
125         //
126         Method method = tm.getMethod();
127         Class methodClass = method.getDeclaringClass();
128         String fullMethodName =  methodClass.getName()
129                 + "."
130                 + method.getName();
131 
132         String[] fullyQualifiedMethodName = new String[] { fullMethodName };
133 
134         //
135         // Iterate through all the classes so we can gather all the included and
136         // excluded methods
137         //
138         for (XmlClass xmlClass : m_classes) {
139           // Only consider included/excluded methods that belong to the same class
140           // we are looking at
141           Class cls = xmlClass.getSupportClass();
142           if(!assignable(methodClass, cls)) {
143             continue;
144           }
145 
146           List<String> includedMethods =
147               createQualifiedMethodNames(xmlClass, toStringList(xmlClass.getIncludedMethods()));
148           boolean isIncludedInMethods = isIncluded(fullyQualifiedMethodName, includedMethods);
149           List<String> excludedMethods = createQualifiedMethodNames(xmlClass,
150               xmlClass.getExcludedMethods());
151           boolean isExcludedInMethods = isExcluded(fullyQualifiedMethodName, excludedMethods);
152           if (result) {
153             // If we're about to include this method by group, make sure
154             // it's included by method and not excluded by method
155             result = isIncludedInMethods && ! isExcludedInMethods;
156           }
157           // otherwise it's already excluded and nothing will bring it back,
158           // since exclusions preempt inclusions
159         }
160       }
161     }
162 
163     Package pkg = m.getDeclaringClass().getPackage();
164     String methodName = pkg != null ? pkg.getName() + "." + m.getName() : m.getName();
165 
166     logInclusion(result ? "Including" : "Excluding", "method", methodName + "()");
167 
168     return result;
169   }
170 
171   @SuppressWarnings({"unchecked"})
assignable(Class sourceClass, Class targetClass)172   private boolean assignable(Class sourceClass, Class targetClass) {
173     return sourceClass.isAssignableFrom(targetClass) || targetClass.isAssignableFrom(sourceClass);
174   }
175 
176   private Map<String, String> m_logged = Maps.newHashMap();
logInclusion(String including, String type, String name)177   private void logInclusion(String including, String type, String name) {
178     if (! m_logged.containsKey(name)) {
179       log(4, including + " " + type + " " + name);
180       m_logged.put(name, name);
181     }
182   }
183 
hasIncludedMethods()184   private boolean hasIncludedMethods() {
185     for (XmlClass xmlClass : m_classes) {
186       if (xmlClass.getIncludedMethods().size() > 0) {
187         return true;
188       }
189     }
190 
191     return false;
192   }
193 
hasExcludedMethods()194   private boolean hasExcludedMethods() {
195     for (XmlClass xmlClass : m_classes) {
196       if (xmlClass.getExcludedMethods().size() > 0) {
197         return true;
198       }
199     }
200 
201     return false;
202   }
203 
toStringList(List<XmlInclude> methods)204   private List<String> toStringList(List<XmlInclude> methods) {
205     List<String> result = Lists.newArrayList();
206     for (XmlInclude m : methods) {
207       result.add(m.getName());
208     }
209     return result;
210   }
211 
createQualifiedMethodNames(XmlClass xmlClass, List<String> methods)212   private List<String> createQualifiedMethodNames(XmlClass xmlClass,
213       List<String> methods) {
214     List<String> vResult = Lists.newArrayList();
215     Class cls = xmlClass.getSupportClass();
216 
217     while (null != cls) {
218       for (String im : methods) {
219         String methodName = im;
220         Method[] allMethods = cls.getDeclaredMethods();
221         Pattern pattern = Pattern.compile(methodName);
222         for (Method m : allMethods) {
223           if (pattern.matcher(m.getName()).matches()) {
224             vResult.add(makeMethodName(cls.getName(), m.getName()));
225           }
226         }
227       }
228       cls = cls.getSuperclass();
229     }
230 
231     return vResult;
232   }
233 
makeMethodName(String className, String methodName)234   private String makeMethodName(String className, String methodName) {
235     return className + "." + methodName;
236   }
237 
checkMethod(Class<?> c, String methodName)238   private void checkMethod(Class<?> c, String methodName) {
239     Pattern p = Pattern.compile(methodName);
240     for (Method m : c.getMethods()) {
241       if (p.matcher(m.getName()).matches()) {
242         return;
243       }
244     }
245     Utils.log("Warning", 2, "The regular expression \"" + methodName + "\" didn't match any" +
246               " method in class " + c.getName());
247   }
248 
setXmlClasses(List<XmlClass> classes)249   public void setXmlClasses(List<XmlClass> classes) {
250     m_classes = classes;
251     for (XmlClass c : classes) {
252       for (XmlInclude m : c.getIncludedMethods()) {
253         checkMethod(c.getSupportClass(), m.getName());
254         String methodName = makeMethodName(c.getName(), m.getName());
255         m_includedMethods.put(methodName, m);
256       }
257     }
258   }
259 
260   /**
261    * @return Returns the excludedGroups.
262    */
getExcludedGroups()263   public Map<String, String> getExcludedGroups() {
264     return m_excludedGroups;
265   }
266 
267   /**
268    * @return Returns the includedGroups.
269    */
getIncludedGroups()270   public Map<String, String> getIncludedGroups() {
271     return m_includedGroups;
272   }
273 
274   /**
275    * @param excludedGroups The excludedGroups to set.
276    */
setExcludedGroups(Map<String, String> excludedGroups)277   public void setExcludedGroups(Map<String, String> excludedGroups) {
278     m_excludedGroups = excludedGroups;
279   }
280 
281   /**
282    * @param includedGroups The includedGroups to set.
283    */
setIncludedGroups(Map<String, String> includedGroups)284   public void setIncludedGroups(Map<String, String> includedGroups) {
285     m_includedGroups = includedGroups;
286   }
287 
isIncluded(String[] groups, Collection<String> includedGroups)288   private static boolean isIncluded(String[] groups, Collection<String> includedGroups) {
289     if (includedGroups.size() == 0) {
290       return true;
291     }
292     else {
293       return isMemberOf(groups, includedGroups);
294     }
295   }
296 
isExcluded(String[] groups, Collection<String> excludedGroups)297   private static boolean isExcluded(String[] groups, Collection<String> excludedGroups) {
298     return isMemberOf(groups, excludedGroups);
299   }
300 
301   /**
302    *
303    * @param groups Array of groups on the method
304    * @param list Map of regexps of groups to be run
305    */
isMemberOf(String[] groups, Collection<String> list)306   private static boolean isMemberOf(String[] groups, Collection<String> list) {
307     for (String group : groups) {
308       for (Object o : list) {
309         String regexpStr = o.toString();
310         boolean match = Pattern.matches(regexpStr, group);
311         if (match) {
312           return true;
313         }
314       }
315     }
316 
317     return false;
318   }
319 
log(int level, String s)320   private static void log(int level, String s) {
321     Utils.log("XmlMethodSelector", level, s);
322   }
323 
ppp(String s)324   private static void ppp(String s) {
325     System.out.println("[XmlMethodSelector] " + s);
326   }
327 
setExpression(String expression)328   public void setExpression(String expression) {
329     m_expression = expression;
330   }
331 
332   private boolean m_isInitialized = false;
333   private List<ITestNGMethod> m_testMethods = null;
334 
335   @Override
setTestMethods(List<ITestNGMethod> testMethods)336   public void setTestMethods(List<ITestNGMethod> testMethods) {
337     // Caution: this variable is initialized with an empty list first and then modified
338     // externally by the caller (TestRunner#fixMethodWithClass). Ugly.
339     m_testMethods = testMethods;
340   }
341 
init(IMethodSelectorContext context)342   private void init(IMethodSelectorContext context) {
343     String[] groups = m_includedGroups.keySet().toArray(new String[m_includedGroups.size()]);
344     Set<String> groupClosure = new HashSet<>();
345     Set<ITestNGMethod> methodClosure = new HashSet<>();
346 
347     List<ITestNGMethod> includedMethods = Lists.newArrayList();
348     for (ITestNGMethod m : m_testMethods) {
349       if (includeMethod(context, m, true)) {
350         includedMethods.add(m);
351       }
352     }
353     MethodGroupsHelper.findGroupTransitiveClosure(this, includedMethods, m_testMethods,
354         groups, groupClosure, methodClosure);
355 
356     // If we are asked to include or exclude specific groups, calculate
357     // the transitive closure of all the included groups.  If no include groups
358     // were specified, don't do anything.
359     // Any group that is part of the transitive closure but not part of
360     // m_includedGroups is being added implicitly by TestNG so that if someone
361     // includes a group z that depends on a, b and c, they don't need to
362     // include a, b and c explicitly.
363     if (m_includedGroups.size() > 0) {
364       // Make the transitive closure our new included groups
365       for (String g : groupClosure) {
366         log(4, "Including group "
367             + (m_includedGroups.containsKey(g) ?
368                 ": " : "(implicitly): ") + g);
369         m_includedGroups.put(g, g);
370       }
371 
372       // Make the transitive closure our new included methods
373       for (ITestNGMethod m : methodClosure) {
374         String methodName =
375          m.getMethod().getDeclaringClass().getName() + "." + m.getMethodName();
376 //        m_includedMethods.add(methodName);
377         List<XmlInclude> includeList = m_includedMethods.get(methodName);
378         XmlInclude xi = new XmlInclude(methodName);
379         // TODO: set the XmlClass on this xi or we won't get inheritance of parameters
380         m_includedMethods.put(methodName, xi);
381         logInclusion("Including", "method ", methodName);
382       }
383     }
384   }
385 }
386