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