org.testng.internal.MethodInheritance 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
A testing framework for the JVM
package org.testng.internal;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.testng.ITestNGMethod;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
public class MethodInheritance {
/**
* A Custom comparator that helps in {@link ITestNGMethod} ordering keeping in mind the class
* hierarchy. Here's how the comparator works:
* Lets say we have two method objects o1 and o2.
* o1 is associated with MyClass and o2 is associated with AnotherClass.
*
*
* - -1 is returned if MyClass is the parent of AnotherClass
*
- 1 is returned if AnotherClass is the parent of MyClass
*
- 0 is returned otherwise if MyClass and AnotherClass are the same i.e., both methods
* belong to the same class.
*
*
* Working of isAssignableFrom
* Lets say we have :
*
*
* - interface Oven
*
- Microwave implements Oven
*
*
*
* - microwave instanceof Oven : returns true
*
- Oven.class.isAssignableFrom(microwave.getClass()) : returns true
*
*/
private static final Comparator COMPARATOR =
(o1, o2) -> {
int result = -2;
Class thisClass = o1.getRealClass();
Class otherClass = o2.getRealClass();
if (thisClass.isAssignableFrom(otherClass)) {
result = -1;
} else if (otherClass.isAssignableFrom(thisClass)) {
result = 1;
} else if (o1.equals(o2)) {
result = 0;
}
return result;
};
/** Look in map for a class that is a superclass of methodClass */
private static List findMethodListSuperClass(
Map> map, Class methodClass) {
for (Map.Entry> 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 */
private static Class findSubClass(
Map> map, Class 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> map = Maps.newHashMap();
//
// Put the list of methods in their hierarchy buckets
//
for (ITestNGMethod method : methods) {
Class methodClass = method.getRealClass();
List 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 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 internalDependencyExists(m1, m2, methods) || 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)
*/
private static void sortMethodsByInheritance(
List methods, boolean baseClassToChild) {
methods.sort(COMPARATOR);
if (!baseClassToChild) {
Collections.reverse(methods);
}
}
}