1 package org.testng.reporters;
2 
3 import org.testng.IResultMap;
4 import org.testng.ISuiteResult;
5 import org.testng.ITestContext;
6 import org.testng.ITestResult;
7 import org.testng.Reporter;
8 import org.testng.annotations.Test;
9 import org.testng.collections.Lists;
10 import org.testng.collections.Maps;
11 import org.testng.collections.Sets;
12 import org.testng.internal.ConstructorOrMethod;
13 import org.testng.internal.Utils;
14 import org.testng.util.Strings;
15 
16 import java.io.File;
17 import java.text.SimpleDateFormat;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Properties;
24 import java.util.Set;
25 
26 /**
27  * Utility writing an ISuiteResult to an XMLStringBuffer. Depending on the settings in the <code>config</code> property
28  * it might generate an additional XML file with the actual content and only reference the file with an <code>url</code>
29  * attribute in the passed XMLStringBuffer.
30  *
31  * @author Cosmin Marginean, Mar 16, 2007
32  */
33 
34 public class XMLSuiteResultWriter {
35 
36   private XMLReporterConfig config;
37 
XMLSuiteResultWriter(XMLReporterConfig config)38   public XMLSuiteResultWriter(XMLReporterConfig config) {
39     this.config = config;
40   }
41 
42   /**
43    * Writes the specified ISuiteResult in the given XMLStringBuffer. Please consider that depending on the settings in
44    * the <code>config</code> property it might generate an additional XML file with the actual content and only
45    * reference the file with an <code>url</code> attribute in the passed XMLStringBuffer.
46    *
47    * @param xmlBuffer   The XML buffer where to write or reference the suite result
48    * @param suiteResult The <code>ISuiteResult</code> to serialize
49    */
writeSuiteResult(XMLStringBuffer xmlBuffer, ISuiteResult suiteResult)50   public void writeSuiteResult(XMLStringBuffer xmlBuffer, ISuiteResult suiteResult) {
51     if (XMLReporterConfig.FF_LEVEL_SUITE_RESULT != config.getFileFragmentationLevel()) {
52       writeAllToBuffer(xmlBuffer, suiteResult);
53     } else {
54       String parentDir =
55               config.getOutputDirectory() + File.separatorChar + suiteResult.getTestContext().getSuite().getName();
56       File file = referenceSuiteResult(xmlBuffer, parentDir, suiteResult);
57       XMLStringBuffer suiteXmlBuffer = new XMLStringBuffer();
58       writeAllToBuffer(suiteXmlBuffer, suiteResult);
59       Utils.writeUtf8File(file.getAbsoluteFile().getParent(), file.getName(), suiteXmlBuffer.toXML());
60     }
61   }
62 
writeAllToBuffer(XMLStringBuffer xmlBuffer, ISuiteResult suiteResult)63   private void writeAllToBuffer(XMLStringBuffer xmlBuffer, ISuiteResult suiteResult) {
64     xmlBuffer.push(XMLReporterConfig.TAG_TEST, getSuiteResultAttributes(suiteResult));
65     Set<ITestResult> testResults = Sets.newHashSet();
66     ITestContext testContext = suiteResult.getTestContext();
67     addAllTestResults(testResults, testContext.getPassedTests());
68     addAllTestResults(testResults, testContext.getFailedTests());
69     addAllTestResults(testResults, testContext.getSkippedTests());
70     addAllTestResults(testResults, testContext.getPassedConfigurations());
71     addAllTestResults(testResults, testContext.getSkippedConfigurations());
72     addAllTestResults(testResults, testContext.getFailedConfigurations());
73     addAllTestResults(testResults, testContext.getFailedButWithinSuccessPercentageTests());
74     addTestResults(xmlBuffer, testResults);
75     xmlBuffer.pop();
76   }
77 
78   @SuppressWarnings("unchecked")
addAllTestResults(Set<ITestResult> testResults, IResultMap resultMap)79   private void addAllTestResults(Set<ITestResult> testResults, IResultMap resultMap) {
80     if (resultMap != null) {
81       // Sort the results chronologically before adding them
82       List<ITestResult> allResults = new ArrayList<>();
83       allResults.addAll(resultMap.getAllResults());
84 
85       Collections.sort(new ArrayList(allResults), new Comparator<ITestResult>() {
86         @Override
87         public int compare(ITestResult o1, ITestResult o2) {
88           return (int) (o1.getStartMillis() - o2.getStartMillis());
89         }
90       });
91 
92       testResults.addAll(allResults);
93     }
94   }
95 
referenceSuiteResult(XMLStringBuffer xmlBuffer, String parentDir, ISuiteResult suiteResult)96   private File referenceSuiteResult(XMLStringBuffer xmlBuffer, String parentDir, ISuiteResult suiteResult) {
97     Properties attrs = new Properties();
98     String suiteResultName = suiteResult.getTestContext().getName() + ".xml";
99     attrs.setProperty(XMLReporterConfig.ATTR_URL, suiteResultName);
100     xmlBuffer.addEmptyElement(XMLReporterConfig.TAG_TEST, attrs);
101     return new File(parentDir + File.separatorChar + suiteResultName);
102   }
103 
getSuiteResultAttributes(ISuiteResult suiteResult)104   private Properties getSuiteResultAttributes(ISuiteResult suiteResult) {
105     Properties attributes = new Properties();
106     ITestContext tc = suiteResult.getTestContext();
107     attributes.setProperty(XMLReporterConfig.ATTR_NAME, tc.getName());
108     XMLReporter.addDurationAttributes(config, attributes, tc.getStartDate(), tc.getEndDate());
109     return attributes;
110   }
111 
addTestResults(XMLStringBuffer xmlBuffer, Set<ITestResult> testResults)112   private void addTestResults(XMLStringBuffer xmlBuffer, Set<ITestResult> testResults) {
113     Map<String, List<ITestResult>> testsGroupedByClass = buildTestClassGroups(testResults);
114     for (Map.Entry<String, List<ITestResult>> result : testsGroupedByClass.entrySet()) {
115       Properties attributes = new Properties();
116       String className = result.getKey();
117       if (config.isSplitClassAndPackageNames()) {
118         int dot = className.lastIndexOf('.');
119         attributes.setProperty(XMLReporterConfig.ATTR_NAME,
120                 dot > -1 ? className.substring(dot + 1, className.length()) : className);
121         attributes.setProperty(XMLReporterConfig.ATTR_PACKAGE, dot > -1 ? className.substring(0, dot) : "[default]");
122       } else {
123         attributes.setProperty(XMLReporterConfig.ATTR_NAME, className);
124       }
125 
126       xmlBuffer.push(XMLReporterConfig.TAG_CLASS, attributes);
127       List<ITestResult> sortedResults = result.getValue();
128       Collections.sort( sortedResults );
129       for (ITestResult testResult : sortedResults) {
130         addTestResult(xmlBuffer, testResult);
131       }
132       xmlBuffer.pop();
133     }
134   }
135 
buildTestClassGroups(Set<ITestResult> testResults)136   private Map<String, List<ITestResult>> buildTestClassGroups(Set<ITestResult> testResults) {
137     Map<String, List<ITestResult>> map = Maps.newHashMap();
138     for (ITestResult result : testResults) {
139       String className = result.getTestClass().getName();
140       List<ITestResult> list = map.get(className);
141       if (list == null) {
142         list = Lists.newArrayList();
143         map.put(className, list);
144       }
145       list.add(result);
146     }
147     return map;
148   }
149 
addTestResult(XMLStringBuffer xmlBuffer, ITestResult testResult)150   private void addTestResult(XMLStringBuffer xmlBuffer, ITestResult testResult) {
151     Properties attribs = getTestResultAttributes(testResult);
152     attribs.setProperty(XMLReporterConfig.ATTR_STATUS, getStatusString(testResult.getStatus()));
153     xmlBuffer.push(XMLReporterConfig.TAG_TEST_METHOD, attribs);
154     addTestMethodParams(xmlBuffer, testResult);
155     addTestResultException(xmlBuffer, testResult);
156     addTestResultOutput(xmlBuffer, testResult);
157     if (config.isGenerateTestResultAttributes()) {
158       addTestResultAttributes(xmlBuffer, testResult);
159     }
160     xmlBuffer.pop();
161   }
162 
getStatusString(int testResultStatus)163   private String getStatusString(int testResultStatus) {
164     switch (testResultStatus) {
165       case ITestResult.SUCCESS:
166         return "PASS";
167       case ITestResult.FAILURE:
168         return "FAIL";
169       case ITestResult.SKIP:
170         return "SKIP";
171       case ITestResult.SUCCESS_PERCENTAGE_FAILURE:
172         return "SUCCESS_PERCENTAGE_FAILURE";
173     }
174     return null;
175   }
176 
getTestResultAttributes(ITestResult testResult)177   private Properties getTestResultAttributes(ITestResult testResult) {
178     Properties attributes = new Properties();
179     if (!testResult.getMethod().isTest()) {
180       attributes.setProperty(XMLReporterConfig.ATTR_IS_CONFIG, "true");
181     }
182     attributes.setProperty(XMLReporterConfig.ATTR_NAME, testResult.getMethod().getMethodName());
183     String testInstanceName = testResult.getTestName();
184     if (null != testInstanceName) {
185       attributes.setProperty(XMLReporterConfig.ATTR_TEST_INSTANCE_NAME, testInstanceName);
186     }
187     String description = testResult.getMethod().getDescription();
188     if (!Utils.isStringEmpty(description)) {
189       attributes.setProperty(XMLReporterConfig.ATTR_DESC, description);
190     }
191 
192     attributes.setProperty(XMLReporterConfig.ATTR_METHOD_SIG, removeClassName(testResult.getMethod().toString()));
193 
194     SimpleDateFormat format = new SimpleDateFormat(config.getTimestampFormat());
195     String startTime = format.format(testResult.getStartMillis());
196     String endTime = format.format(testResult.getEndMillis());
197     attributes.setProperty(XMLReporterConfig.ATTR_STARTED_AT, startTime);
198     attributes.setProperty(XMLReporterConfig.ATTR_FINISHED_AT, endTime);
199     long duration = testResult.getEndMillis() - testResult.getStartMillis();
200     String strDuration = Long.toString(duration);
201     attributes.setProperty(XMLReporterConfig.ATTR_DURATION_MS, strDuration);
202 
203     if (config.isGenerateGroupsAttribute()) {
204       String groupNamesStr = Utils.arrayToString(testResult.getMethod().getGroups());
205       if (!Utils.isStringEmpty(groupNamesStr)) {
206         attributes.setProperty(XMLReporterConfig.ATTR_GROUPS, groupNamesStr);
207       }
208     }
209 
210     if (config.isGenerateDependsOnMethods()) {
211       String dependsOnStr = Utils.arrayToString(testResult.getMethod().getMethodsDependedUpon());
212       if (!Utils.isStringEmpty(dependsOnStr)) {
213         attributes.setProperty(XMLReporterConfig.ATTR_DEPENDS_ON_METHODS, dependsOnStr);
214       }
215     }
216 
217     if (config.isGenerateDependsOnGroups()) {
218       String dependsOnStr = Utils.arrayToString(testResult.getMethod().getGroupsDependedUpon());
219       if (!Utils.isStringEmpty(dependsOnStr)) {
220         attributes.setProperty(XMLReporterConfig.ATTR_DEPENDS_ON_GROUPS, dependsOnStr);
221       }
222     }
223 
224     ConstructorOrMethod cm = testResult.getMethod().getConstructorOrMethod();
225     Test testAnnotation;
226     if (cm.getMethod() != null) {
227       testAnnotation = cm.getMethod().getAnnotation(Test.class);
228       if (testAnnotation != null) {
229         String dataProvider = testAnnotation.dataProvider();
230         if (!Strings.isNullOrEmpty(dataProvider)) {
231           attributes.setProperty(XMLReporterConfig.ATTR_DATA_PROVIDER, dataProvider);
232         }
233       }
234     }
235 
236     return attributes;
237   }
238 
removeClassName(String methodSignature)239   private String removeClassName(String methodSignature) {
240     int firstParanthesisPos = methodSignature.indexOf("(");
241     int dotAferClassPos = methodSignature.substring(0, firstParanthesisPos).lastIndexOf(".");
242     return methodSignature.substring(dotAferClassPos + 1, methodSignature.length());
243   }
244 
addTestMethodParams(XMLStringBuffer xmlBuffer, ITestResult testResult)245   public void addTestMethodParams(XMLStringBuffer xmlBuffer, ITestResult testResult) {
246     Object[] parameters = testResult.getParameters();
247     if ((parameters != null) && (parameters.length > 0)) {
248       xmlBuffer.push(XMLReporterConfig.TAG_PARAMS);
249       for (int i = 0; i < parameters.length; i++) {
250         addParameter(xmlBuffer, parameters[i], i);
251       }
252       xmlBuffer.pop();
253     }
254   }
255 
addParameter(XMLStringBuffer xmlBuffer, Object parameter, int i)256   private void addParameter(XMLStringBuffer xmlBuffer, Object parameter, int i) {
257     Properties attrs = new Properties();
258     attrs.setProperty(XMLReporterConfig.ATTR_INDEX, String.valueOf(i));
259     xmlBuffer.push(XMLReporterConfig.TAG_PARAM, attrs);
260     if (parameter == null) {
261       Properties valueAttrs = new Properties();
262       valueAttrs.setProperty(XMLReporterConfig.ATTR_IS_NULL, "true");
263       xmlBuffer.addEmptyElement(XMLReporterConfig.TAG_PARAM_VALUE, valueAttrs);
264     } else {
265       xmlBuffer.push(XMLReporterConfig.TAG_PARAM_VALUE);
266       xmlBuffer.addCDATA(parameter.toString());
267       xmlBuffer.pop();
268     }
269     xmlBuffer.pop();
270   }
271 
addTestResultException(XMLStringBuffer xmlBuffer, ITestResult testResult)272   private void addTestResultException(XMLStringBuffer xmlBuffer, ITestResult testResult) {
273     Throwable exception = testResult.getThrowable();
274     if (exception != null) {
275       Properties exceptionAttrs = new Properties();
276       exceptionAttrs.setProperty(XMLReporterConfig.ATTR_CLASS, exception.getClass().getName());
277       xmlBuffer.push(XMLReporterConfig.TAG_EXCEPTION, exceptionAttrs);
278 
279       if (!Utils.isStringEmpty(exception.getMessage())) {
280         xmlBuffer.push(XMLReporterConfig.TAG_MESSAGE);
281         xmlBuffer.addCDATA(exception.getMessage());
282         xmlBuffer.pop();
283       }
284 
285       String[] stackTraces = Utils.stackTrace(exception, false);
286       if ((config.getStackTraceOutputMethod() & XMLReporterConfig.STACKTRACE_SHORT) == XMLReporterConfig
287               .STACKTRACE_SHORT) {
288         xmlBuffer.push(XMLReporterConfig.TAG_SHORT_STACKTRACE);
289         xmlBuffer.addCDATA(stackTraces[0]);
290         xmlBuffer.pop();
291       }
292       if ((config.getStackTraceOutputMethod() & XMLReporterConfig.STACKTRACE_FULL) == XMLReporterConfig
293               .STACKTRACE_FULL) {
294         xmlBuffer.push(XMLReporterConfig.TAG_FULL_STACKTRACE);
295         xmlBuffer.addCDATA(stackTraces[1]);
296         xmlBuffer.pop();
297       }
298 
299       xmlBuffer.pop();
300     }
301   }
302 
addTestResultOutput(XMLStringBuffer xmlBuffer, ITestResult testResult)303   private void addTestResultOutput(XMLStringBuffer xmlBuffer, ITestResult testResult) {
304     // TODO: Cosmin - maybe a <line> element isn't indicated for each line
305     xmlBuffer.push(XMLReporterConfig.TAG_REPORTER_OUTPUT);
306     List<String> output = Reporter.getOutput(testResult);
307     for (String line : output) {
308       if (line != null) {
309         xmlBuffer.push(XMLReporterConfig.TAG_LINE);
310         xmlBuffer.addCDATA(line);
311         xmlBuffer.pop();
312       }
313     }
314     xmlBuffer.pop();
315   }
316 
addTestResultAttributes(XMLStringBuffer xmlBuffer, ITestResult testResult)317   private void addTestResultAttributes(XMLStringBuffer xmlBuffer, ITestResult testResult) {
318     if (testResult.getAttributeNames() != null && testResult.getAttributeNames().size() > 0) {
319       xmlBuffer.push(XMLReporterConfig.TAG_ATTRIBUTES);
320       for (String attrName: testResult.getAttributeNames()) {
321         if (attrName == null) {
322           continue;
323         }
324         Object attrValue = testResult.getAttribute(attrName);
325 
326         Properties attributeAttrs = new Properties();
327         attributeAttrs.setProperty(XMLReporterConfig.ATTR_NAME, attrName);
328         if (attrValue == null) {
329           attributeAttrs.setProperty(XMLReporterConfig.ATTR_IS_NULL, "true");
330           xmlBuffer.addEmptyElement(XMLReporterConfig.TAG_ATTRIBUTE, attributeAttrs);
331         } else {
332           xmlBuffer.push(XMLReporterConfig.TAG_ATTRIBUTE, attributeAttrs);
333           xmlBuffer.addCDATA(attrValue.toString());
334           xmlBuffer.pop();
335         }
336       }
337       xmlBuffer.pop();
338     }
339   }
340 
341 }
342