XMLReporter.java revision dc0051b2828ddc734f076f959a1d9d54c322654d
1package org.testng.reporters;
2
3import org.testng.*;
4import org.testng.internal.Utils;
5import org.testng.xml.XmlSuite;
6
7import java.io.File;
8import java.util.*;
9import java.text.SimpleDateFormat;
10
11/**
12 * The main entry for the XML generation operation
13 *
14 * @author Cosmin Marginean, Mar 16, 2007
15 */
16public class XMLReporter implements IReporter {
17
18  private XMLReporterConfig config = new XMLReporterConfig();
19  private XMLStringBuffer rootBuffer;
20
21  public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
22    if (Utils.isStringEmpty(config.getOutputDirectory())) {
23      config.setOutputDirectory(outputDirectory);
24    }
25
26    rootBuffer = new XMLStringBuffer("");
27    rootBuffer.push(XMLReporterConfig.TAG_TESTNG_RESULTS);
28    writeReporterOutput(rootBuffer);
29    for (int i = 0; i < suites.size(); i++) {
30      writeSuite(suites.get(i).getXmlSuite(), suites.get(i));
31    }
32    rootBuffer.pop();
33    Utils.writeUtf8File(config.getOutputDirectory(), "testng-results.xml", rootBuffer.toXML());
34  }
35
36  private void writeReporterOutput(XMLStringBuffer xmlBuffer) {
37    //TODO: Cosmin - maybe a <line> element isn't indicated for each line
38    xmlBuffer.push(XMLReporterConfig.TAG_REPORTER_OUTPUT);
39    List<String> output = Reporter.getOutput();
40    for (String line : output) {
41      if (line != null) {
42        xmlBuffer.push(XMLReporterConfig.TAG_LINE);
43        xmlBuffer.addCDATA(line);
44        xmlBuffer.pop();
45      }
46    }
47    xmlBuffer.pop();
48  }
49
50  private void writeSuite(XmlSuite xmlSuite, ISuite suite) {
51    switch (config.getFileFragmentationLevel()) {
52      case XMLReporterConfig.FF_LEVEL_NONE:
53        writeSuiteToBuffer(rootBuffer, suite);
54        break;
55      case XMLReporterConfig.FF_LEVEL_SUITE:
56      case XMLReporterConfig.FF_LEVEL_SUITE_RESULT:
57        File suiteFile = referenceSuite(rootBuffer, suite);
58        writeSuiteToFile(suiteFile, suite);
59    }
60  }
61
62  private void writeSuiteToFile(File suiteFile, ISuite suite) {
63    XMLStringBuffer xmlBuffer = new XMLStringBuffer("");
64    writeSuiteToBuffer(xmlBuffer, suite);
65    File parentDir = suiteFile.getParentFile();
66    if (parentDir.exists() || suiteFile.getParentFile().mkdirs()) {
67      Utils.writeFile(parentDir.getAbsolutePath(), "testng-results.xml", xmlBuffer.toXML());
68    }
69  }
70
71  private File referenceSuite(XMLStringBuffer xmlBuffer, ISuite suite) {
72    String relativePath = suite.getName() + File.separatorChar + "testng-results.xml";
73    File suiteFile = new File(config.getOutputDirectory(), relativePath);
74    Properties attrs = new Properties();
75    attrs.setProperty(XMLReporterConfig.ATTR_URL, relativePath);
76    xmlBuffer.addEmptyElement(XMLReporterConfig.TAG_SUITE, attrs);
77    return suiteFile;
78  }
79
80  private void writeSuiteToBuffer(XMLStringBuffer xmlBuffer, ISuite suite) {
81    xmlBuffer.push(XMLReporterConfig.TAG_SUITE, getSuiteAttributes(suite));
82    writeSuiteGroups(xmlBuffer, suite);
83
84    Map<String, ISuiteResult> results = suite.getResults();
85    XMLSuiteResultWriter suiteResultWriter = new XMLSuiteResultWriter(config);
86    for (Map.Entry<String, ISuiteResult> result : results.entrySet()) {
87      suiteResultWriter.writeSuiteResult(xmlBuffer, result.getValue());
88    }
89
90    xmlBuffer.pop();
91  }
92
93  private void writeSuiteGroups(XMLStringBuffer xmlBuffer, ISuite suite) {
94    xmlBuffer.push(XMLReporterConfig.TAG_GROUPS);
95    Map<String, Collection<ITestNGMethod>> methodsByGroups = suite.getMethodsByGroups();
96    for (String groupName : methodsByGroups.keySet()) {
97      Properties groupAttrs = new Properties();
98      groupAttrs.setProperty(XMLReporterConfig.ATTR_NAME, groupName);
99      xmlBuffer.push(XMLReporterConfig.TAG_GROUP, groupAttrs);
100      Set<ITestNGMethod> groupMethods = getUniqueMethodSet(methodsByGroups.get(groupName));
101      for (ITestNGMethod groupMethod : groupMethods) {
102        Properties methodAttrs = new Properties();
103        methodAttrs.setProperty(XMLReporterConfig.ATTR_NAME, groupMethod.getMethodName());
104        methodAttrs.setProperty(XMLReporterConfig.ATTR_METHOD_SIG, groupMethod.toString());
105        methodAttrs.setProperty(XMLReporterConfig.ATTR_CLASS, groupMethod.getRealClass().getName());
106        xmlBuffer.addEmptyElement(XMLReporterConfig.TAG_METHOD, methodAttrs);
107      }
108      xmlBuffer.pop();
109    }
110    xmlBuffer.pop();
111  }
112
113  private Properties getSuiteAttributes(ISuite suite) {
114    Properties props = new Properties();
115    props.setProperty(XMLReporterConfig.ATTR_NAME, suite.getName());
116
117    // Calculate the duration
118    Map<String, ISuiteResult> results = suite.getResults();
119    Date minStartDate = new Date();
120    Date maxEndDate = new Date();
121    // TODO: We could probably optimize this in order not to traverse this twice
122    for (Map.Entry<String, ISuiteResult> result : results.entrySet()) {
123      Date startDate = result.getValue().getTestContext().getStartDate();
124      Date endDate = result.getValue().getTestContext().getEndDate();
125      if (minStartDate.after(startDate)) {
126        minStartDate = startDate;
127      }
128      if (maxEndDate == null || maxEndDate.before(endDate)) {
129        maxEndDate = endDate;
130      }
131    }
132
133    addDurationAttributes(config, props, minStartDate, maxEndDate);
134    return props;
135  }
136
137  /**
138   * Add started-at, finished-at and duration-ms attributes to the <suite> tag
139   */
140  public static void addDurationAttributes(XMLReporterConfig config, Properties attributes,
141      Date minStartDate, Date maxEndDate) {
142    SimpleDateFormat format = new SimpleDateFormat(config.getTimestampFormat());
143    String startTime = format.format(minStartDate);
144    String endTime = format.format(maxEndDate);
145    long duration = maxEndDate.getTime() - minStartDate.getTime();
146
147    attributes.setProperty(XMLReporterConfig.ATTR_STARTED_AT, startTime);
148    attributes.setProperty(XMLReporterConfig.ATTR_FINISHED_AT, endTime);
149    attributes.setProperty(XMLReporterConfig.ATTR_DURATION_MS, Long.toString(duration));
150  }
151
152  private Set<ITestNGMethod> getUniqueMethodSet(Collection<ITestNGMethod> methods) {
153    Set<ITestNGMethod> result = new LinkedHashSet<ITestNGMethod>();
154    for (ITestNGMethod method : methods) {
155      result.add(method);
156    }
157    return result;
158  }
159
160  //TODO: This is not the smartest way to implement the config
161  public int getFileFragmentationLevel() {
162    return config.getFileFragmentationLevel();
163  }
164
165  public void setFileFragmentationLevel(int fileFragmentationLevel) {
166    config.setFileFragmentationLevel(fileFragmentationLevel);
167  }
168
169  public int getStackTraceOutputMethod() {
170    return config.getStackTraceOutputMethod();
171  }
172
173  public void setStackTraceOutputMethod(int stackTraceOutputMethod) {
174    config.setStackTraceOutputMethod(stackTraceOutputMethod);
175  }
176
177  public String getOutputDirectory() {
178    return config.getOutputDirectory();
179  }
180
181  public void setOutputDirectory(String outputDirectory) {
182    config.setOutputDirectory(outputDirectory);
183  }
184
185  public boolean isGenerateGroupsAttribute() {
186    return config.isGenerateGroupsAttribute();
187  }
188
189  public void setGenerateGroupsAttribute(boolean generateGroupsAttribute) {
190    config.setGenerateGroupsAttribute(generateGroupsAttribute);
191  }
192
193  public boolean isSplitClassAndPackageNames() {
194    return config.isSplitClassAndPackageNames();
195  }
196
197  public void setSplitClassAndPackageNames(boolean splitClassAndPackageNames) {
198    config.setSplitClassAndPackageNames(splitClassAndPackageNames);
199  }
200
201  public String getTimestampFormat() {
202    return config.getTimestampFormat();
203  }
204
205  public void setTimestampFormat(String timestampFormat) {
206    config.setTimestampFormat(timestampFormat);
207  }
208
209  public boolean isGenerateDependsOnMethods() {
210    return config.isGenerateDependsOnMethods();
211  }
212
213  public void setGenerateDependsOnMethods(boolean generateDependsOnMethods) {
214    config.setGenerateDependsOnMethods(generateDependsOnMethods);
215  }
216
217  public void setGenerateDependsOnGroups(boolean generateDependsOnGroups) {
218    config.setGenerateDependsOnGroups(generateDependsOnGroups);
219  }
220
221  public boolean isGenerateDependsOnGroups() {
222    return config.isGenerateDependsOnGroups();
223  }
224}
225