• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.testng.internal;
2 
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.Method;
5 import java.lang.reflect.Modifier;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Set;
9 
10 import org.testng.IClass;
11 import org.testng.IInstanceInfo;
12 import org.testng.ITestContext;
13 import org.testng.ITestObjectFactory;
14 import org.testng.TestNGException;
15 import org.testng.annotations.IAnnotation;
16 import org.testng.collections.Lists;
17 import org.testng.collections.Maps;
18 import org.testng.internal.annotations.AnnotationHelper;
19 import org.testng.internal.annotations.IAnnotationFinder;
20 import org.testng.xml.XmlClass;
21 import org.testng.xml.XmlTest;
22 
23 import static org.testng.internal.ClassHelper.getAvailableMethods;
24 
25 /**
26  * This class creates an ITestClass from a test class.
27  *
28  * @author <a href="mailto:cedric@beust.com">Cedric Beust</a>
29  */
30 public class TestNGClassFinder extends BaseClassFinder {
31   private ITestContext m_testContext = null;
32   private Map<Class, List<Object>> m_instanceMap = Maps.newHashMap();
33 
TestNGClassFinder(ClassInfoMap cim, Map<Class, List<Object>> instanceMap, XmlTest xmlTest, IConfiguration configuration, ITestContext testContext)34   public TestNGClassFinder(ClassInfoMap cim,
35                            Map<Class, List<Object>> instanceMap,
36                            XmlTest xmlTest,
37                            IConfiguration configuration,
38                            ITestContext testContext)
39   {
40     m_testContext = testContext;
41 
42     if(null == instanceMap) {
43       instanceMap= Maps.newHashMap();
44     }
45 
46     IAnnotationFinder annotationFinder = configuration.getAnnotationFinder();
47     ITestObjectFactory objectFactory = configuration.getObjectFactory();
48 
49     //
50     // Find all the new classes and their corresponding instances
51     //
52     Set<Class<?>> allClasses= cim.getClasses();
53 
54     //very first pass is to find ObjectFactory, can't create anything else until then
55     if(objectFactory == null) {
56       objectFactory = new ObjectFactoryImpl();
57       outer:
58       for (Class cls : allClasses) {
59         try {
60           if (null != cls) {
61             Method[] ms;
62             try {
63               ms = cls.getMethods();
64             } catch (NoClassDefFoundError e) {
65               // https://github.com/cbeust/testng/issues/602
66               ppp("Warning: Can't link and determine methods of " + cls);
67               ms = new Method[0];
68             }
69             for (Method m : ms) {
70               IAnnotation a = annotationFinder.findAnnotation(m,
71                   org.testng.annotations.IObjectFactoryAnnotation.class);
72               if (null != a) {
73                 if (!ITestObjectFactory.class.isAssignableFrom(m.getReturnType())) {
74                   throw new TestNGException("Return type of " + m + " is not IObjectFactory");
75                 }
76                 try {
77                   Object instance = cls.newInstance();
78                   if (m.getParameterTypes().length > 0 && m.getParameterTypes()[0].equals(ITestContext.class)) {
79                     objectFactory = (ITestObjectFactory) m.invoke(instance, testContext);
80                   } else {
81                     objectFactory = (ITestObjectFactory) m.invoke(instance);
82                   }
83                   break outer;
84                 }
85                 catch (Exception ex) {
86                   throw new TestNGException("Error creating object factory: " + cls,
87                       ex);
88                 }
89               }
90             }
91           }
92         } catch (NoClassDefFoundError e) {
93           Utils.log("[TestNGClassFinder]", 1, "Unable to read methods on class " + cls.getName()
94               + " - unable to resolve class reference " + e.getMessage());
95 
96           for (XmlClass xmlClass : xmlTest.getXmlClasses()) {
97             if (xmlClass.loadClasses() && xmlClass.getName().equals(cls.getName())) {
98               throw e;
99             }
100           }
101 
102         }
103       }
104     }
105 
106     for(Class cls : allClasses) {
107       if((null == cls)) {
108         ppp("FOUND NULL CLASS IN FOLLOWING ARRAY:");
109         int i= 0;
110         for(Class c : allClasses) {
111           ppp("  " + i + ": " + c);
112         }
113 
114         continue;
115       }
116 
117       if(isTestNGClass(cls, annotationFinder)) {
118         List allInstances= instanceMap.get(cls);
119         Object thisInstance= (null != allInstances) ? allInstances.get(0) : null;
120 
121         // If annotation class and instances are abstract, skip them
122         if ((null == thisInstance) && Modifier.isAbstract(cls.getModifiers())) {
123           Utils.log("", 5, "[WARN] Found an abstract class with no valid instance attached: " + cls);
124           continue;
125         }
126 
127         IClass ic= findOrCreateIClass(m_testContext, cls, cim.getXmlClass(cls), thisInstance,
128             xmlTest, annotationFinder, objectFactory);
129         if(null != ic) {
130           Object[] theseInstances = ic.getInstances(false);
131           if (theseInstances.length == 0) {
132             theseInstances = ic.getInstances(true);
133           }
134           Object instance= theseInstances[0];
135           putIClass(cls, ic);
136 
137           ConstructorOrMethod factoryMethod =
138             ClassHelper.findDeclaredFactoryMethod(cls, annotationFinder);
139           if (factoryMethod != null && factoryMethod.getEnabled()) {
140             FactoryMethod fm = new FactoryMethod( /* cls, */
141               factoryMethod,
142               instance,
143               xmlTest,
144               annotationFinder,
145               m_testContext);
146             ClassInfoMap moreClasses = new ClassInfoMap();
147 
148             {
149 //            ppp("INVOKING FACTORY " + fm + " " + this.hashCode());
150               Object[] instances= fm.invoke();
151 
152               //
153               // If the factory returned IInstanceInfo, get the class from it,
154               // otherwise, just call getClass() on the returned instances
155               //
156               if (instances.length > 0) {
157                 if (instances[0] != null) {
158                   Class elementClass = instances[0].getClass();
159                   if(IInstanceInfo.class.isAssignableFrom(elementClass)) {
160                     for(Object o : instances) {
161                       IInstanceInfo ii = (IInstanceInfo) o;
162                       addInstance(ii.getInstanceClass(), ii.getInstance());
163                       moreClasses.addClass(ii.getInstanceClass());
164                     }
165                   }
166                   else {
167                     for (int i = 0; i < instances.length; i++) {
168                       Object o = instances[i];
169                       if (o == null) {
170                         throw new TestNGException("The factory " + fm + " returned a null instance" +
171                             "at index " + i);
172                       } else {
173                         addInstance(o.getClass(), o);
174                         if(!classExists(o.getClass())) {
175                           moreClasses.addClass(o.getClass());
176                         }
177                       }
178                     }
179                   }
180                 }
181               }
182             }
183 
184             if(moreClasses.getSize() > 0) {
185               TestNGClassFinder finder=
186                 new TestNGClassFinder(moreClasses,
187                     m_instanceMap,
188                     xmlTest,
189                     configuration,
190                     m_testContext);
191 
192               IClass[] moreIClasses= finder.findTestClasses();
193               for(IClass ic2 : moreIClasses) {
194                 putIClass(ic2.getRealClass(), ic2);
195               }
196             } // if moreClasses.size() > 0
197           }
198         } // null != ic
199       } // if not TestNG class
200       else {
201         Utils.log("TestNGClassFinder", 3, "SKIPPING CLASS " + cls + " no TestNG annotations found");
202       }
203     } // for
204 
205     //
206     // Add all the instances we found to their respective IClasses
207     //
208     for(Map.Entry<Class, List<Object>> entry : m_instanceMap.entrySet()) {
209       Class clazz = entry.getKey();
210       for(Object instance : entry.getValue()) {
211         IClass ic= getIClass(clazz);
212         if(null != ic) {
213           ic.addInstance(instance);
214         }
215       }
216     }
217   }
218 
219   /**
220    * @return true if this class contains TestNG annotations (either on itself
221    * or on a superclass).
222    */
isTestNGClass(Class<?> c, IAnnotationFinder annotationFinder)223   public static boolean isTestNGClass(Class<?> c, IAnnotationFinder annotationFinder) {
224     Class[] allAnnotations= AnnotationHelper.getAllAnnotations();
225     Class<?> cls = c;
226 
227     try {
228       for(Class annotation : allAnnotations) {
229 
230         for (cls = c; cls != null; cls = cls.getSuperclass()) {
231           // Try on the methods
232           for (Method m : getAvailableMethods(cls)) {
233             IAnnotation ma= annotationFinder.findAnnotation(m, annotation);
234             if(null != ma) {
235               return true;
236             }
237           }
238 
239           // Try on the class
240           IAnnotation a= annotationFinder.findAnnotation(cls, annotation);
241           if(null != a) {
242             return true;
243           }
244 
245           // Try on the constructors
246           for (Constructor ctor : cls.getConstructors()) {
247             IAnnotation ca= annotationFinder.findAnnotation(ctor, annotation);
248             if(null != ca) {
249               return true;
250             }
251           }
252         }
253       }
254 
255       return false;
256 
257     } catch (NoClassDefFoundError e) {
258       Utils.log("[TestNGClassFinder]", 1,
259           "Unable to read methods on class " + cls.getName()
260           + " - unable to resolve class reference " + e.getMessage());
261       return false;
262     }
263   }
264 
addInstance(Class clazz, Object o)265   private void addInstance(Class clazz, Object o) {
266     List<Object> list= m_instanceMap.get(clazz);
267 
268     if(null == list) {
269       list= Lists.newArrayList();
270       m_instanceMap.put(clazz, list);
271     }
272 
273     list.add(o);
274   }
275 
ppp(String s)276   public static void ppp(String s) {
277     System.out.println("[TestNGClassFinder] " + s);
278   }
279 
280 }
281