• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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