blob: 75a041afd34d80eee3985d9aecfeae551f7367c7 [file] [log] [blame]
package org.testng.internal;
import org.testng.ITestNGMethod;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class MethodInheritance {
/**
* Look in map for a class that is a superclass of methodClass
* @param map
* @param methodClass
* @return
*/
private static List<ITestNGMethod> findMethodListSuperClass(Map<Class, List<ITestNGMethod>> map,
Class< ? extends ITestNGMethod> methodClass)
{
for (Map.Entry<Class, List<ITestNGMethod>> entry : map.entrySet()) {
if (entry.getKey().isAssignableFrom(methodClass)) {
return entry.getValue();
}
}
return null;
}
/**
* Look in map for a class that is a subclass of methodClass
* @param map
* @param methodClass
* @return
*/
private static Class findSubClass(Map<Class, List<ITestNGMethod>> map,
Class< ? extends ITestNGMethod> methodClass)
{
for (Class cls : map.keySet()) {
if (methodClass.isAssignableFrom(cls)) {
return cls;
}
}
return null;
}
/**
* Fix the methodsDependedUpon to make sure that @Configuration methods
* respect inheritance (before methods are invoked in the order Base first
* and after methods are invoked in the order Child first)
*
* @param methods the list of methods
* @param before true if we are handling a before method (meaning, the methods
* need to be sorted base class first and subclass last). false otherwise (subclass
* methods first, base classes last).
*/
public static void fixMethodInheritance(ITestNGMethod[] methods, boolean before) {
// Map of classes -> List of methods that belong to this class or same hierarchy
Map<Class, List<ITestNGMethod>> map = Maps.newHashMap();
//
// Put the list of methods in their hierarchy buckets
//
for (ITestNGMethod method : methods) {
Class< ? extends ITestNGMethod> methodClass = method.getRealClass();
List<ITestNGMethod> l = findMethodListSuperClass(map, methodClass);
if (null != l) {
l.add(method);
}
else {
Class subClass = findSubClass(map, methodClass);
if (null != subClass) {
l = map.get(subClass);
l.add(method);
map.remove(subClass);
map.put(methodClass, l);
}
else {
l = Lists.newArrayList();
l.add(method);
map.put(methodClass, l);
}
}
}
//
// Each bucket that has a list bigger than one element gets sorted
//
for (List<ITestNGMethod> l : map.values()) {
if (l.size() > 1) {
// Sort them
sortMethodsByInheritance(l, before);
/*
* Set methodDependedUpon accordingly
* E.g. Base class can have multiple @BeforeClass methods. Need to ensure
* that @BeforeClass methods in derived class depend on all @BeforeClass methods
* of base class. Vice versa for @AfterXXX methods
*/
for (int i = 0; i < l.size() - 1; i++) {
ITestNGMethod m1 = l.get(i);
for (int j = i + 1; j < l.size(); j++) {
ITestNGMethod m2 = l.get(j);
if (!equalsEffectiveClass(m1, m2) && !dependencyExists(m1, m2, methods)) {
Utils.log("MethodInheritance", 4, m2 + " DEPENDS ON " + m1);
m2.addMethodDependedUpon(MethodHelper.calculateMethodCanonicalName(m1));
}
}
}
}
}
}
private static boolean dependencyExists(ITestNGMethod m1, ITestNGMethod m2, ITestNGMethod[] methods) {
return true == internalDependencyExists(m1, m2, methods)
? true : internalDependencyExists(m2, m1, methods);
}
private static boolean internalDependencyExists(ITestNGMethod m1, ITestNGMethod m2, ITestNGMethod[] methods) {
ITestNGMethod[] methodsNamed =
MethodHelper.findDependedUponMethods(m1, methods);
for (ITestNGMethod method : methodsNamed) {
if (method.equals(m2)) {
return true;
}
}
for (String group : m1.getGroupsDependedUpon()) {
ITestNGMethod[] methodsThatBelongToGroup =
MethodGroupsHelper.findMethodsThatBelongToGroup(m1, methods, group);
for (ITestNGMethod method : methodsThatBelongToGroup) {
if (method.equals(m2)) {
return true;
}
}
}
return false;
}
private static boolean equalsEffectiveClass(ITestNGMethod m1, ITestNGMethod m2) {
try {
Class c1 = m1.getRealClass();
Class c2 = m2.getRealClass();
return c1 == null ? c2 == null : c1.equals(c2);
}
catch(Exception ex) {
return false;
}
}
/**
* Given a list of methods belonging to the same class hierarchy, orders them
* from the base class to the child (if true) or from child to base class (if false)
* @param methods
*/
private static void sortMethodsByInheritance(List<ITestNGMethod> methods,
boolean baseClassToChild)
{
Collections.sort(methods);
if (! baseClassToChild) {
Collections.reverse(methods);
}
}
}