...23import org.testng.Reporter;24import org.testng.collections.ListMultiMap;25import org.testng.collections.Lists;26import org.testng.collections.Maps;27import org.testng.collections.SetMultiMap;28import org.testng.collections.Sets;29import org.testng.internal.Utils;30import org.testng.xml.XmlSuite;31public class JUnitReportReporter implements IReporter {32 @Override33 public void generateReport(34 List<XmlSuite> xmlSuites, List<ISuite> suites, String defaultOutputDirectory) {35 Map<Class<?>, Set<ITestResult>> results = Maps.newHashMap();36 ListMultiMap<Object, ITestResult> befores = Maps.newListMultiMap();37 ListMultiMap<Object, ITestResult> afters = Maps.newListMultiMap();38 SetMultiMap<Class<?>, ITestNGMethod> mapping = new SetMultiMap<>(false);39 for (ISuite suite : suites) {40 Map<String, ISuiteResult> suiteResults = suite.getResults();41 addMapping(mapping, suite.getExcludedMethods());42 for (ISuiteResult sr : suiteResults.values()) {43 ITestContext tc = sr.getTestContext();44 addResults(tc.getPassedTests().getAllResults(), results);45 addResults(tc.getFailedTests().getAllResults(), results);46 addResults(tc.getSkippedTests().getAllResults(), results);47 addResults(tc.getFailedConfigurations().getAllResults(), results);48 for (ITestResult tr : tc.getPassedConfigurations().getAllResults()) {49 if (tr.getMethod().isBeforeMethodConfiguration()) {50 befores.put(tr.getInstance(), tr);51 }52 if (tr.getMethod().isAfterMethodConfiguration()) {53 afters.put(tr.getInstance(), tr);54 }55 }56 }57 }58 for (Map.Entry<Class<?>, Set<ITestResult>> entry : results.entrySet()) {59 Class<?> cls = entry.getKey();60 Properties p1 = new Properties();61 p1.setProperty(XMLConstants.ATTR_NAME, cls.getName());62 p1.setProperty(XMLConstants.ATTR_TIMESTAMP, JUnitXMLReporter.formattedTime());63 List<TestTag> testCases = Lists.newArrayList();64 int failures = 0;65 int errors = 0;66 int skipped = 0;67 int testCount = 0;68 float totalTime = 0;69 Collection<ITestResult> iTestResults = sort(entry.getValue());70 for (ITestResult tr : iTestResults) {71 long time = tr.getEndMillis() - tr.getStartMillis();72 time += getNextConfiguration(befores, tr);73 time += getNextConfiguration(afters, tr);74 Throwable t = tr.getThrowable();75 switch (tr.getStatus()) {76 case ITestResult.SKIP:77 case ITestResult.SUCCESS_PERCENTAGE_FAILURE:78 skipped++;79 break;80 case ITestResult.FAILURE:81 if (t instanceof AssertionError) {82 failures++;83 } else {84 errors++;85 }86 break;87 }88 totalTime += time;89 testCount++;90 TestTag testTag = createTestTagFor(tr, cls);91 testTag.properties.setProperty(XMLConstants.ATTR_TIME, "" + formatTime(time));92 testCases.add(testTag);93 }94 int ignored = getDisabledTestCount(mapping.get(entry.getKey()));95 for (ITestNGMethod eachMethod : mapping.get(entry.getKey())) {96 testCases.add(createIgnoredTestTagFor(eachMethod));97 }98 p1.setProperty(XMLConstants.ATTR_FAILURES, Integer.toString(failures));99 p1.setProperty(XMLConstants.ATTR_ERRORS, Integer.toString(errors));100 p1.setProperty(XMLConstants.SKIPPED, Integer.toString(skipped + ignored));101 p1.setProperty(XMLConstants.ATTR_NAME, cls.getName());102 p1.setProperty(XMLConstants.ATTR_TESTS, Integer.toString(testCount + ignored));103 p1.setProperty(XMLConstants.ATTR_TIME, "" + formatTime(totalTime));104 try {105 p1.setProperty(XMLConstants.ATTR_HOSTNAME, InetAddress.getLocalHost().getHostName());106 } catch (UnknownHostException e) {107 // ignore108 }109 //110 // Now that we have all the information we need, generate the file111 //112 XMLStringBuffer xsb = new XMLStringBuffer();113 xsb.addComment("Generated by " + getClass().getName());114 xsb.push(XMLConstants.TESTSUITE, p1);115 for (TestTag testTag : testCases) {116 if (putElement(xsb, XMLConstants.TESTCASE, testTag.properties, testTag.childTag != null)) {117 Properties p = new Properties();118 safeSetProperty(p, XMLConstants.ATTR_MESSAGE, testTag.message);119 safeSetProperty(p, XMLConstants.ATTR_TYPE, testTag.type);120 if (putElement(xsb, testTag.childTag, p, testTag.stackTrace != null)) {121 xsb.addCDATA(testTag.stackTrace);122 xsb.pop(testTag.childTag);123 }124 xsb.pop(XMLConstants.TESTCASE);125 }126 if (putElement(xsb, XMLConstants.SYSTEM_OUT, new Properties(), testTag.sysOut != null)) {127 xsb.addCDATA(testTag.sysOut);128 xsb.pop(XMLConstants.SYSTEM_OUT);129 }130 }131 xsb.pop(XMLConstants.TESTSUITE);132 String outputDirectory = defaultOutputDirectory + File.separator + "junitreports";133 Utils.writeUtf8File(outputDirectory, getFileName(cls), xsb.toXML());134 }135 }136 private static Collection<ITestResult> sort(Set<ITestResult> results) {137 List<ITestResult> sortedResults = new ArrayList<>(results);138 sortedResults.sort(Comparator.comparingInt(o -> o.getMethod().getPriority()));139 return Collections.unmodifiableList(sortedResults);140 }141 private static int getDisabledTestCount(Set<ITestNGMethod> methods) {142 int count = 0;143 for (ITestNGMethod method : methods) {144 if (!method.getEnabled()) {145 count = count + 1;146 }147 }148 return count;149 }150 private TestTag createIgnoredTestTagFor(ITestNGMethod method) {151 TestTag testTag = new TestTag();152 Properties p2 = new Properties();153 p2.setProperty(XMLConstants.ATTR_CLASSNAME, method.getRealClass().getName());154 p2.setProperty(XMLConstants.ATTR_NAME, method.getMethodName());155 testTag.childTag = XMLConstants.SKIPPED;156 testTag.properties = p2;157 return testTag;158 }159 private TestTag createTestTagFor(ITestResult tr, Class<?> cls) {160 TestTag testTag = new TestTag();161 Properties p2 = new Properties();162 p2.setProperty(XMLConstants.ATTR_CLASSNAME, cls.getName());163 p2.setProperty(XMLConstants.ATTR_NAME, getTestName(tr));164 int status = tr.getStatus();165 if (status == ITestResult.SKIP || status == ITestResult.SUCCESS_PERCENTAGE_FAILURE) {166 testTag.childTag = XMLConstants.SKIPPED;167 } else if (status == ITestResult.FAILURE) {168 handleFailure(testTag, tr.getThrowable());169 }170 List<String> output = Reporter.getOutput(tr);171 if (!output.isEmpty()) {172 testTag.sysOut = String.join("\n", output);173 }174 testTag.properties = p2;175 return testTag;176 }177 private static void handleFailure(TestTag testTag, Throwable t) {178 testTag.childTag = t instanceof AssertionError ? XMLConstants.FAILURE : XMLConstants.ERROR;179 if (t != null) {180 StringWriter sw = new StringWriter();181 PrintWriter pw = new PrintWriter(sw);182 t.printStackTrace(pw);183 testTag.message = t.getMessage();184 testTag.type = t.getClass().getName();185 testTag.stackTrace = sw.toString();186 }187 }188 /** Put a XML start or empty tag to the XMLStringBuffer depending on hasChildElements parameter */189 private boolean putElement(190 XMLStringBuffer xsb, String tagName, Properties attributes, boolean hasChildElements) {191 if (hasChildElements) {192 xsb.push(tagName, attributes);193 } else {194 xsb.addEmptyElement(tagName, attributes);195 }196 return hasChildElements;197 }198 /** Set property if value is non-null */199 private void safeSetProperty(Properties p, String key, String value) {200 if (value != null) {201 p.setProperty(key, value);202 }203 }204 /**205 * Add the time of the configuration method to this test method.206 *207 * <p>The only problem with this method is that the timing of a test method might not be added to208 * the time of the same configuration method that ran before it but since they should all be209 * equivalent, this should never be an issue.210 */211 private long getNextConfiguration(212 ListMultiMap<Object, ITestResult> configurations, ITestResult tr) {213 long result = 0;214 List<ITestResult> confResults = configurations.get(tr.getInstance());215 Map<ITestNGMethod, ITestResult> seen = Maps.newHashMap();216 for (ITestResult r : confResults) {217 if (!seen.containsKey(r.getMethod())) {218 result += r.getEndMillis() - r.getStartMillis();219 seen.put(r.getMethod(), r);220 }221 }222 confResults.removeAll(seen.values());223 return result;224 }225 protected String getFileName(Class cls) {226 return "TEST-" + cls.getName() + ".xml";227 }228 protected String getTestName(ITestResult tr) {229 return tr.getMethod().getMethodName();230 }231 private String formatTime(float time) {232 DecimalFormatSymbols symbols = new DecimalFormatSymbols();233 // JUnitReports wants points here, regardless of the locale234 symbols.setDecimalSeparator('.');235 DecimalFormat format = new DecimalFormat("#.###", symbols);236 format.setMinimumFractionDigits(3);237 return format.format(time / 1000.0f);238 }239 private static class TestTag {240 Properties properties;241 String message;242 String type;243 String stackTrace;244 String childTag;245 String sysOut;246 }247 private void addResults(Set<ITestResult> allResults, Map<Class<?>, Set<ITestResult>> out) {248 for (ITestResult tr : allResults) {249 Class<?> cls = tr.getMethod().getTestClass().getRealClass();250 Set<ITestResult> l = out.computeIfAbsent(cls, k -> Sets.newHashSet());251 l.add(tr);252 }253 }254 private void addMapping(255 SetMultiMap<Class<?>, ITestNGMethod> mapping, Collection<ITestNGMethod> methods) {256 for (ITestNGMethod method : methods) {257 if (!method.getEnabled()) {258 mapping.put(method.getRealClass(), method);259 }260 }261 }262}...