org.testng.internal.XmlMethodSelector 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 org.testng.IMethodSelector;
import org.testng.IMethodSelectorContext;
import org.testng.ITestNGMethod;
import org.testng.TestNGException;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlInclude;
import bsh.EvalError;
import bsh.Interpreter;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
/**
* This class is the default method selector used by TestNG to determine
* which methods need to be included and excluded based on the specification
* given in testng.xml.
*
* Created on Sep 30, 2005
* @author cbeust
*/
public class XmlMethodSelector implements IMethodSelector {
private static final long serialVersionUID = -9030548178025605629L;
private static Interpreter s_interpreter;
// Groups included and excluded for this run
private Map m_includedGroups = Maps.newHashMap();
private Map m_excludedGroups = Maps.newHashMap();
private List m_classes = null;
// The BeanShell expression for this test, if any
private String m_expression = null;
// List of methods included implicitly
private Set m_includedMethods = new HashSet();
@Override
public boolean includeMethod(IMethodSelectorContext context,
ITestNGMethod tm, boolean isTestMethod)
{
// ppp("XML METHOD SELECTOR " + tm + " " + m_isInitialized);
if (! m_isInitialized) {
m_isInitialized = true;
init(context);
}
boolean result = false;
if (null != m_expression) {
result = includeMethodFromExpression(tm, isTestMethod);
}
else {
result = includeMethodFromIncludeExclude(tm, isTestMethod);
}
return result;
}
private static Interpreter getInterpreter() {
if(null == s_interpreter) {
s_interpreter= new Interpreter();
}
return s_interpreter;
}
private boolean includeMethodFromExpression(ITestNGMethod tm, boolean isTestMethod) {
boolean result = false;
Interpreter interpreter= getInterpreter();
try {
Map groups = Maps.newHashMap();
for (String group : tm.getGroups()) {
groups.put(group, group);
}
setContext(interpreter, tm.getMethod(), groups, tm);
Object evalResult = interpreter.eval(m_expression);
result = ((Boolean) evalResult).booleanValue();
}
catch (EvalError evalError) {
Utils.log("bsh.Interpreter", 2, "Cannot evaluate expression:" + m_expression + ":" + evalError.getMessage());
}
finally {
resetContext(interpreter);
}
return result;
}
private void resetContext(Interpreter interpreter) {
try {
interpreter.unset("method");
interpreter.unset("groups");
interpreter.unset("testngMethod");
}
catch(EvalError evalError) {
Utils.log("bsh.Interpreter", 2, "Cannot reset interpreter:" + evalError.getMessage());
}
}
private void setContext(Interpreter interpreter, Method method, Map groups, ITestNGMethod tm) {
try {
interpreter.set("method", method);
interpreter.set("groups", groups);
interpreter.set("testngMethod", tm);
}
catch(EvalError evalError) {
throw new TestNGException("Cannot set BSH interpreter", evalError);
}
}
private boolean includeMethodFromIncludeExclude(ITestNGMethod tm, boolean isTestMethod) {
boolean result = false;
Method m = tm.getMethod();
String[] groups = tm.getGroups();
Map includedGroups = m_includedGroups;
Map excludedGroups = m_excludedGroups;
//
// No groups were specified:
//
if (includedGroups.size() == 0 && excludedGroups.size() == 0
&& ! hasIncludedMethods() && ! hasExcludedMethods())
//
// If we don't include or exclude any methods, method is in
//
{
result = true;
}
//
// If it's a configuration method and no groups were requested, we want it in
//
else if (includedGroups.size() == 0 && excludedGroups.size() == 0 && ! isTestMethod)
{
result = true;
}
//
// Is this method included implicitly?
//
else if (m_includedMethods.contains(MethodHelper.calculateMethodCanonicalName(tm))) {
result = true;
}
//
// Include or Exclude groups were specified:
//
else {
//
// Only add this method if it belongs to an included group and not
// to an excluded group
//
{
boolean isIncludedInGroups = isIncluded(groups, m_includedGroups.values());
boolean isExcludedInGroups = isExcluded(groups, m_excludedGroups.values());
//
// Calculate the run methods by groups first
//
if (isIncludedInGroups && !isExcludedInGroups) {
result = true;
}
else if (isExcludedInGroups) {
result = false;
}
}
if(isTestMethod) {
//
// Now filter by method name
//
Method method = tm.getMethod();
Class methodClass = method.getDeclaringClass();
String fullMethodName = methodClass.getName()
+ "."
+ method.getName();
String[] fullyQualifiedMethodName = new String[] { fullMethodName };
//
// Iterate through all the classes so we can gather all the included and
// excluded methods
//
for (XmlClass xmlClass : m_classes) {
// Only consider included/excluded methods that belong to the same class
// we are looking at
Class cls = ClassHelper.forName(xmlClass.getName());
if(null == cls) {
Utils.log("XmlMethodSelector", 1, "Cannot find class in classpath " + xmlClass.getName());
continue;
}
if(!assignable(methodClass, cls)) {
continue;
}
List includedMethods =
createQualifiedMethodNames(xmlClass, toStringList(xmlClass.getIncludedMethods()));
boolean isIncludedInMethods = isIncluded(fullyQualifiedMethodName, includedMethods);
List excludedMethods = createQualifiedMethodNames(xmlClass,
xmlClass.getExcludedMethods());
boolean isExcludedInMethods = isExcluded(fullyQualifiedMethodName, excludedMethods);
if (result) {
// If we're about to include this method by group, make sure
// it's included by method and not excluded by method
result = isIncludedInMethods && ! isExcludedInMethods;
}
// otherwise it's already excluded and nothing will bring it back,
// since exclusions preempt inclusions
}
}
}
Package pkg = m.getDeclaringClass().getPackage();
String methodName = pkg != null ? pkg.getName() + "." + m.getName() : m.getName();
logInclusion(result ? "Including" : "Excluding", "method", methodName + "()");
return result;
}
@SuppressWarnings({"unchecked"})
private boolean assignable(Class sourceClass, Class targetClass) {
return sourceClass.isAssignableFrom(targetClass) || targetClass.isAssignableFrom(sourceClass);
}
private Map m_logged = Maps.newHashMap();
private void logInclusion(String including, String type, String name) {
if (! m_logged.containsKey(name)) {
log(4, including + " " + type + " " + name);
m_logged.put(name, name);
}
}
private boolean hasIncludedMethods() {
for (XmlClass xmlClass : m_classes) {
if (xmlClass.getIncludedMethods().size() > 0) {
return true;
}
}
return false;
}
private boolean hasExcludedMethods() {
for (XmlClass xmlClass : m_classes) {
if (xmlClass.getExcludedMethods().size() > 0) {
return true;
}
}
return false;
}
private List toStringList(List methods) {
List result = Lists.newArrayList();
for (XmlInclude m : methods) {
result.add(m.getName());
}
return result;
}
private List createQualifiedMethodNames(XmlClass xmlClass,
List methods) {
List vResult = Lists.newArrayList();
Class cls = xmlClass.getSupportClass();
while (null != cls) {
for (String im : methods) {
String methodName = im;
Method[] allMethods = cls.getDeclaredMethods();
Pattern pattern = Pattern.compile(methodName);
for (Method m : allMethods) {
if (pattern.matcher(m.getName()).matches()) {
vResult.add(makeMethodName(cls.getName(), m.getName()));
}
}
}
cls = cls.getSuperclass();
}
return vResult;
}
private String makeMethodName(String className, String methodName) {
return className + "." + methodName;
}
public void setXmlClasses(List classes) {
m_classes = classes;
for (XmlClass c : classes) {
for (XmlInclude m : c.getIncludedMethods()) {
m_includedMethods.add(makeMethodName(c.getName(), m.getName()));
}
}
}
/**
* @return Returns the excludedGroups.
*/
public Map getExcludedGroups() {
return m_excludedGroups;
}
/**
* @return Returns the includedGroups.
*/
public Map getIncludedGroups() {
return m_includedGroups;
}
/**
* @param excludedGroups The excludedGroups to set.
*/
public void setExcludedGroups(Map excludedGroups) {
m_excludedGroups = excludedGroups;
}
/**
* @param includedGroups The includedGroups to set.
*/
public void setIncludedGroups(Map includedGroups) {
m_includedGroups = includedGroups;
}
private static boolean isIncluded(String[] groups, Collection includedGroups) {
if (includedGroups.size() == 0) {
return true;
}
else {
return isMemberOf(groups, includedGroups);
}
}
private static boolean isExcluded(String[] groups, Collection excludedGroups) {
return isMemberOf(groups, excludedGroups);
}
/**
*
* @param groups Array of groups on the method
* @param list Map of regexps of groups to be run
*/
private static boolean isMemberOf(String[] groups, Collection list) {
for (String group : groups) {
for (Object o : list) {
String regexpStr = o.toString();
boolean match = Pattern.matches(regexpStr, group);
if (match) {
return true;
}
}
}
return false;
}
private static void log(int level, String s) {
Utils.log("XmlMethodSelector", level, s);
}
private static void ppp(String s) {
System.out.println("[XmlMethodSelector] " + s);
}
public void setExpression(String expression) {
m_expression = expression;
}
private boolean m_isInitialized = false;
private List m_testMethods = null;
@Override
public void setTestMethods(List testMethods) {
// Caution: this variable is initialized with an empty list first and then modified
// externally by the caller (TestRunner#fixMethodWithClass). Ugly.
m_testMethods = testMethods;
}
private void init(IMethodSelectorContext context) {
String[] groups = m_includedGroups.keySet().toArray(new String[m_includedGroups.size()]);
Set groupClosure = new HashSet();
Set methodClosure = new HashSet();
List includedMethods = Lists.newArrayList();
for (ITestNGMethod m : m_testMethods) {
if (includeMethod(context, m, true)) {
includedMethods.add(m);
}
}
MethodGroupsHelper.findGroupTransitiveClosure(this, includedMethods, m_testMethods,
groups, groupClosure, methodClosure);
// If we are asked to include or exclude specific groups, calculate
// the transitive closure of all the included groups. If no include groups
// were specified, don't do anything.
// Any group that is part of the transitive closure but not part of
// m_includedGroups is being added implicitly by TestNG so that if someone
// includes a group z that depends on a, b and c, they don't need to
// include a, b and c explicitly.
if (m_includedGroups.size() > 0) {
// Make the transitive closure our new included groups
for (String g : groupClosure) {
log(4, "Including group "
+ (m_includedGroups.containsKey(g) ?
": " : "(implicitly): ") + g);
m_includedGroups.put(g, g);
}
// Make the transitive closure our new included methods
for (ITestNGMethod m : methodClosure) {
String methodName =
m.getMethod().getDeclaringClass().getName() + "." + m.getMethodName();
m_includedMethods.add(methodName);
logInclusion("Including", "method ", methodName);
}
}
}
}