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