1 package org.testng.reporters; 2 3 import org.testng.IReporter; 4 import org.testng.ISuite; 5 import org.testng.ISuiteResult; 6 import org.testng.ITestContext; 7 import org.testng.ITestNGMethod; 8 import org.testng.ITestResult; 9 import org.testng.TestListenerAdapter; 10 import org.testng.collections.Lists; 11 import org.testng.collections.Maps; 12 import org.testng.collections.Sets; 13 import org.testng.internal.MethodHelper; 14 import org.testng.internal.Utils; 15 import org.testng.xml.XmlClass; 16 import org.testng.xml.XmlInclude; 17 import org.testng.xml.XmlSuite; 18 import org.testng.xml.XmlTest; 19 20 import java.util.Collection; 21 import java.util.HashSet; 22 import java.util.List; 23 import java.util.Map; 24 import java.util.Set; 25 26 /** 27 * This reporter is responsible for creating testng-failed.xml 28 * 29 * @author <a href="mailto:cedric@beust.com">Cedric Beust</a> 30 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a> 31 */ 32 public class FailedReporter extends TestListenerAdapter implements IReporter { 33 public static final String TESTNG_FAILED_XML = "testng-failed.xml"; 34 35 private XmlSuite m_xmlSuite; 36 FailedReporter()37 public FailedReporter() { 38 } 39 FailedReporter(XmlSuite xmlSuite)40 public FailedReporter(XmlSuite xmlSuite) { 41 m_xmlSuite = xmlSuite; 42 } 43 44 @Override generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory)45 public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) { 46 for (ISuite suite : suites) { 47 generateFailureSuite(suite.getXmlSuite(), suite, outputDirectory); 48 } 49 } 50 generateFailureSuite(XmlSuite xmlSuite, ISuite suite, String outputDir)51 protected void generateFailureSuite(XmlSuite xmlSuite, ISuite suite, String outputDir) { 52 XmlSuite failedSuite = (XmlSuite) xmlSuite.clone(); 53 failedSuite.setName("Failed suite [" + xmlSuite.getName() + "]"); 54 m_xmlSuite= failedSuite; 55 56 Map<String, XmlTest> xmlTests= Maps.newHashMap(); 57 for(XmlTest xmlT: xmlSuite.getTests()) { 58 xmlTests.put(xmlT.getName(), xmlT); 59 } 60 61 Map<String, ISuiteResult> results = suite.getResults(); 62 63 for(Map.Entry<String, ISuiteResult> entry : results.entrySet()) { 64 ISuiteResult suiteResult = entry.getValue(); 65 ITestContext testContext = suiteResult.getTestContext(); 66 67 generateXmlTest(suite, 68 xmlTests.get(testContext.getName()), 69 testContext, 70 testContext.getFailedTests().getAllResults(), 71 testContext.getSkippedTests().getAllResults()); 72 } 73 74 if(null != failedSuite.getTests() && failedSuite.getTests().size() > 0) { 75 Utils.writeUtf8File(outputDir, TESTNG_FAILED_XML, failedSuite.toXml()); 76 Utils.writeUtf8File(suite.getOutputDirectory(), TESTNG_FAILED_XML, failedSuite.toXml()); 77 } 78 } 79 80 /** 81 * Do not rely on this method. The class is used as <code>IReporter</code>. 82 * 83 * @see org.testng.TestListenerAdapter#onFinish(org.testng.ITestContext) 84 * @deprecated this class is used now as IReporter 85 */ 86 @Deprecated 87 @Override onFinish(ITestContext context)88 public void onFinish(ITestContext context) { 89 // Delete the previous file 90 // File f = new File(context.getOutputDirectory(), getFileName(context)); 91 // f.delete(); 92 93 // Calculate the methods we need to rerun : failed tests and 94 // their dependents 95 // List<ITestResult> failedTests = getFailedTests(); 96 // List<ITestResult> skippedTests = getSkippedTests(); 97 } 98 generateXmlTest(ISuite suite, XmlTest xmlTest, ITestContext context, Collection<ITestResult> failedTests, Collection<ITestResult> skippedTests)99 private void generateXmlTest(ISuite suite, 100 XmlTest xmlTest, 101 ITestContext context, 102 Collection<ITestResult> failedTests, 103 Collection<ITestResult> skippedTests) { 104 // Note: we can have skipped tests and no failed tests 105 // if a method depends on nonexistent groups 106 if (skippedTests.size() > 0 || failedTests.size() > 0) { 107 Set<ITestNGMethod> methodsToReRun = Sets.newHashSet(); 108 109 // Get the transitive closure of all the failed methods and the methods 110 // they depend on 111 Collection[] allTests = new Collection[] { 112 failedTests, skippedTests 113 }; 114 115 for (Collection<ITestResult> tests : allTests) { 116 for (ITestResult failedTest : tests) { 117 ITestNGMethod current = failedTest.getMethod(); 118 if (current.isTest()) { 119 methodsToReRun.add(current); 120 ITestNGMethod method = failedTest.getMethod(); 121 // Don't count configuration methods 122 if (method.isTest()) { 123 List<ITestNGMethod> methodsDependedUpon = 124 MethodHelper.getMethodsDependedUpon(method, context.getAllTestMethods()); 125 126 for (ITestNGMethod m : methodsDependedUpon) { 127 if (m.isTest()) { 128 methodsToReRun.add(m); 129 } 130 } 131 } 132 } 133 } 134 } 135 136 // 137 // Now we have all the right methods. Go through the list of 138 // all the methods that were run and only pick those that are 139 // in the methodToReRun map. Since the methods are already 140 // sorted, we don't need to sort them again. 141 // 142 List<ITestNGMethod> result = Lists.newArrayList(); 143 for (ITestNGMethod m : context.getAllTestMethods()) { 144 if (methodsToReRun.contains(m)) { 145 result.add(m); 146 } 147 } 148 149 methodsToReRun.clear(); 150 Collection<ITestNGMethod> invoked= suite.getInvokedMethods(); 151 for(ITestNGMethod tm: invoked) { 152 if(!tm.isTest()) { 153 methodsToReRun.add(tm); 154 } 155 } 156 157 result.addAll(methodsToReRun); 158 createXmlTest(context, result, xmlTest); 159 } 160 } 161 162 /** 163 * Generate testng-failed.xml 164 */ createXmlTest(ITestContext context, List<ITestNGMethod> methods, XmlTest srcXmlTest)165 private void createXmlTest(ITestContext context, List<ITestNGMethod> methods, XmlTest srcXmlTest) { 166 XmlTest xmlTest = new XmlTest(m_xmlSuite); 167 xmlTest.setName(context.getName() + "(failed)"); 168 xmlTest.setBeanShellExpression(srcXmlTest.getExpression()); 169 xmlTest.setIncludedGroups(srcXmlTest.getIncludedGroups()); 170 xmlTest.setExcludedGroups(srcXmlTest.getExcludedGroups()); 171 xmlTest.setParallel(srcXmlTest.getParallel()); 172 xmlTest.setParameters(srcXmlTest.getLocalParameters()); 173 xmlTest.setJUnit(srcXmlTest.isJUnit()); 174 List<XmlClass> xmlClasses = createXmlClasses(methods, srcXmlTest); 175 xmlTest.setXmlClasses(xmlClasses); 176 } 177 178 /** 179 * @param methods The methods we want to represent 180 * @param srcXmlTest 181 * @return A list of XmlClass objects (each representing a <class> tag) based 182 * on the parameter methods 183 */ createXmlClasses(List<ITestNGMethod> methods, XmlTest srcXmlTest)184 private List<XmlClass> createXmlClasses(List<ITestNGMethod> methods, XmlTest srcXmlTest) { 185 List<XmlClass> result = Lists.newArrayList(); 186 Map<Class, Set<ITestNGMethod>> methodsMap= Maps.newHashMap(); 187 188 for (ITestNGMethod m : methods) { 189 Object[] instances= m.getInstances(); 190 Class clazz= instances == null || instances.length == 0 || instances[0] == null 191 ? m.getRealClass() 192 : instances[0].getClass(); 193 Set<ITestNGMethod> methodList= methodsMap.get(clazz); 194 if(null == methodList) { 195 methodList= new HashSet<>(); 196 methodsMap.put(clazz, methodList); 197 } 198 methodList.add(m); 199 } 200 201 // Ideally, we should preserve each parameter in each class but putting them 202 // all in the same bag for now 203 Map<String, String> parameters = Maps.newHashMap(); 204 for (XmlClass c : srcXmlTest.getClasses()) { 205 parameters.putAll(c.getLocalParameters()); 206 } 207 208 int index = 0; 209 for(Map.Entry<Class, Set<ITestNGMethod>> entry: methodsMap.entrySet()) { 210 Class clazz= entry.getKey(); 211 Set<ITestNGMethod> methodList= entry.getValue(); 212 // @author Borojevic 213 // Need to check all the methods, not just @Test ones. 214 XmlClass xmlClass= new XmlClass(clazz.getName(), index++, false /* don't load classes */); 215 List<XmlInclude> methodNames= Lists.newArrayList(methodList.size()); 216 int ind = 0; 217 for(ITestNGMethod m: methodList) { 218 methodNames.add(new XmlInclude(m.getMethod().getName(), m.getFailedInvocationNumbers(), 219 ind++)); 220 } 221 xmlClass.setIncludedMethods(methodNames); 222 xmlClass.setParameters(parameters); 223 result.add(xmlClass); 224 225 } 226 227 return result; 228 } 229 230 /** 231 * TODO: we might want to make that more flexible in the future, but for 232 * now, hardcode the file name 233 */ getFileName(ITestContext context)234 private String getFileName(ITestContext context) { 235 return TESTNG_FAILED_XML; 236 } 237 ppp(String s)238 private static void ppp(String s) { 239 System.out.println("[FailedReporter] " + s); 240 } 241 } 242