1 package org.testng.internal; 2 3 import java.util.Collections; 4 import java.util.List; 5 import java.util.Map; 6 7 import org.testng.ITestNGMethod; 8 import org.testng.collections.Lists; 9 import org.testng.collections.Maps; 10 11 public class MethodInheritance { 12 /** 13 * Look in map for a class that is a superclass of methodClass 14 */ findMethodListSuperClass(Map<Class, List<ITestNGMethod>> map, Class< ? extends ITestNGMethod> methodClass)15 private static List<ITestNGMethod> findMethodListSuperClass(Map<Class, List<ITestNGMethod>> map, 16 Class< ? extends ITestNGMethod> methodClass) 17 { 18 for (Map.Entry<Class, List<ITestNGMethod>> entry : map.entrySet()) { 19 if (entry.getKey().isAssignableFrom(methodClass)) { 20 return entry.getValue(); 21 } 22 } 23 return null; 24 } 25 26 /** 27 * Look in map for a class that is a subclass of methodClass 28 */ findSubClass(Map<Class, List<ITestNGMethod>> map, Class< ? extends ITestNGMethod> methodClass)29 private static Class findSubClass(Map<Class, List<ITestNGMethod>> map, 30 Class< ? extends ITestNGMethod> methodClass) 31 { 32 for (Class cls : map.keySet()) { 33 if (methodClass.isAssignableFrom(cls)) { 34 return cls; 35 } 36 } 37 38 return null; 39 } 40 41 /** 42 * Fix the methodsDependedUpon to make sure that @Configuration methods 43 * respect inheritance (before methods are invoked in the order Base first 44 * and after methods are invoked in the order Child first) 45 * 46 * @param methods the list of methods 47 * @param before true if we are handling a before method (meaning, the methods 48 * need to be sorted base class first and subclass last). false otherwise (subclass 49 * methods first, base classes last). 50 */ fixMethodInheritance(ITestNGMethod[] methods, boolean before)51 public static void fixMethodInheritance(ITestNGMethod[] methods, boolean before) { 52 // Map of classes -> List of methods that belong to this class or same hierarchy 53 Map<Class, List<ITestNGMethod>> map = Maps.newHashMap(); 54 55 // 56 // Put the list of methods in their hierarchy buckets 57 // 58 for (ITestNGMethod method : methods) { 59 Class< ? extends ITestNGMethod> methodClass = method.getRealClass(); 60 List<ITestNGMethod> l = findMethodListSuperClass(map, methodClass); 61 if (null != l) { 62 l.add(method); 63 } 64 else { 65 Class subClass = findSubClass(map, methodClass); 66 if (null != subClass) { 67 l = map.get(subClass); 68 l.add(method); 69 map.remove(subClass); 70 map.put(methodClass, l); 71 } 72 else { 73 l = Lists.newArrayList(); 74 l.add(method); 75 map.put(methodClass, l); 76 } 77 } 78 } 79 80 // 81 // Each bucket that has a list bigger than one element gets sorted 82 // 83 for (List<ITestNGMethod> l : map.values()) { 84 if (l.size() > 1) { 85 // Sort them 86 sortMethodsByInheritance(l, before); 87 88 /* 89 * Set methodDependedUpon accordingly 90 * E.g. Base class can have multiple @BeforeClass methods. Need to ensure 91 * that @BeforeClass methods in derived class depend on all @BeforeClass methods 92 * of base class. Vice versa for @AfterXXX methods 93 */ 94 for (int i = 0; i < l.size() - 1; i++) { 95 ITestNGMethod m1 = l.get(i); 96 for (int j = i + 1; j < l.size(); j++) { 97 ITestNGMethod m2 = l.get(j); 98 if (!equalsEffectiveClass(m1, m2) && !dependencyExists(m1, m2, methods)) { 99 Utils.log("MethodInheritance", 4, m2 + " DEPENDS ON " + m1); 100 m2.addMethodDependedUpon(MethodHelper.calculateMethodCanonicalName(m1)); 101 } 102 } 103 } 104 } 105 } 106 } 107 dependencyExists(ITestNGMethod m1, ITestNGMethod m2, ITestNGMethod[] methods)108 private static boolean dependencyExists(ITestNGMethod m1, ITestNGMethod m2, ITestNGMethod[] methods) { 109 return internalDependencyExists(m1, m2, methods) || internalDependencyExists(m2, m1, methods); 110 } 111 internalDependencyExists(ITestNGMethod m1, ITestNGMethod m2, ITestNGMethod[] methods)112 private static boolean internalDependencyExists(ITestNGMethod m1, ITestNGMethod m2, ITestNGMethod[] methods) { 113 ITestNGMethod[] methodsNamed = 114 MethodHelper.findDependedUponMethods(m1, methods); 115 116 for (ITestNGMethod method : methodsNamed) { 117 if (method.equals(m2)) { 118 return true; 119 } 120 } 121 122 for (String group : m1.getGroupsDependedUpon()) { 123 ITestNGMethod[] methodsThatBelongToGroup = 124 MethodGroupsHelper.findMethodsThatBelongToGroup(m1, methods, group); 125 for (ITestNGMethod method : methodsThatBelongToGroup) { 126 if (method.equals(m2)) { 127 return true; 128 } 129 } 130 } 131 132 return false; 133 } 134 equalsEffectiveClass(ITestNGMethod m1, ITestNGMethod m2)135 private static boolean equalsEffectiveClass(ITestNGMethod m1, ITestNGMethod m2) { 136 try { 137 Class c1 = m1.getRealClass(); 138 Class c2 = m2.getRealClass(); 139 140 return c1 == null ? c2 == null : c1.equals(c2); 141 } 142 catch(Exception ex) { 143 return false; 144 } 145 } 146 147 148 /** 149 * Given a list of methods belonging to the same class hierarchy, orders them 150 * from the base class to the child (if true) or from child to base class (if false) 151 * @param methods 152 */ sortMethodsByInheritance(List<ITestNGMethod> methods, boolean baseClassToChild)153 private static void sortMethodsByInheritance(List<ITestNGMethod> methods, 154 boolean baseClassToChild) 155 { 156 Collections.sort(methods); 157 if (! baseClassToChild) { 158 Collections.reverse(methods); 159 } 160 } 161 } 162