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 (at) 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 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 */ 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 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 276 public static void ppp(String s) { 277 System.out.println("[TestNGClassFinder] " + s); 278 } 279 280 } 281