1 package org.testng.reporters; 2 3 import static org.testng.internal.Utils.isStringNotEmpty; 4 5 import org.testng.IInvokedMethod; 6 import org.testng.IReporter; 7 import org.testng.ISuite; 8 import org.testng.ISuiteResult; 9 import org.testng.ITestClass; 10 import org.testng.ITestContext; 11 import org.testng.ITestNGMethod; 12 import org.testng.Reporter; 13 import org.testng.collections.Maps; 14 import org.testng.internal.Utils; 15 import org.testng.xml.XmlSuite; 16 17 import java.io.BufferedWriter; 18 import java.io.File; 19 import java.io.IOException; 20 import java.lang.reflect.Method; 21 import java.text.SimpleDateFormat; 22 import java.util.Arrays; 23 import java.util.Collection; 24 import java.util.Collections; 25 import java.util.Comparator; 26 import java.util.List; 27 import java.util.Map; 28 29 /** 30 * This class implements an HTML reporter for suites. 31 * 32 * @author cbeust 33 * @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a> 34 */ 35 public class SuiteHTMLReporter implements IReporter { 36 public static final String METHODS_CHRONOLOGICAL = "methods.html"; 37 public static final String METHODS_ALPHABETICAL = "methods-alphabetical.html"; 38 public static final String GROUPS = "groups.html"; 39 public static final String CLASSES = "classes.html"; 40 public static final String REPORTER_OUTPUT = "reporter-output.html"; 41 public static final String METHODS_NOT_RUN = "methods-not-run.html"; 42 public static final String TESTNG_XML = "testng.xml.html"; 43 44 private Map<String, ITestClass> m_classes = Maps.newHashMap(); 45 private String m_outputDirectory; 46 47 @Override generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory)48 public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) { 49 m_outputDirectory = generateOutputDirectoryName(outputDirectory + File.separator + "old"); 50 51 try { 52 HtmlHelper.generateStylesheet(outputDirectory); 53 } catch (IOException e) { 54 // TODO Propagate the exception properly. 55 e.printStackTrace(); 56 } 57 58 for (ISuite suite : suites) { 59 60 // 61 // Generate the various reports 62 // 63 XmlSuite xmlSuite = suite.getXmlSuite(); 64 if (xmlSuite.getTests().size() == 0) { 65 continue; 66 } 67 generateTableOfContents(xmlSuite, suite); 68 generateSuites(xmlSuite, suite); 69 generateIndex(xmlSuite, suite); 70 generateMain(xmlSuite, suite); 71 generateMethodsAndGroups(xmlSuite, suite); 72 generateMethodsChronologically(xmlSuite, suite, METHODS_CHRONOLOGICAL, false); 73 generateMethodsChronologically(xmlSuite, suite, METHODS_ALPHABETICAL, true); 74 generateClasses(xmlSuite, suite); 75 generateReporterOutput(xmlSuite, suite); 76 generateExcludedMethodsReport(xmlSuite, suite); 77 generateXmlFile(xmlSuite, suite); 78 } 79 80 generateIndex(suites); 81 } 82 83 /** 84 * Overridable by subclasses to create different directory names (e.g. with timestamps). 85 * @param outputDirectory the output directory specified by the user 86 */ generateOutputDirectoryName(String outputDirectory)87 protected String generateOutputDirectoryName(String outputDirectory) { 88 return outputDirectory; 89 } 90 generateXmlFile(XmlSuite xmlSuite, ISuite suite)91 private void generateXmlFile(XmlSuite xmlSuite, ISuite suite) { 92 String content = xmlSuite.toXml().replaceAll("<", "<").replaceAll(">", ">") 93 .replaceAll(" ", " ").replaceAll("\n", "<br/>"); 94 95 StringBuffer sb = new StringBuffer("<html>"); 96 97 sb.append("<head><title>").append("testng.xml for ") 98 .append(xmlSuite.getName()).append("</title></head><body><tt>") 99 .append(content) 100 .append("</tt></body></html>"); 101 102 Utils.writeFile(getOutputDirectory(xmlSuite), TESTNG_XML, sb.toString()); 103 } 104 105 /** 106 * Generate the main index.html file that lists all the suites 107 * and their result 108 */ generateIndex(List<ISuite> suites)109 private void generateIndex(List<ISuite> suites) { 110 StringBuffer sb = new StringBuffer(); 111 String title = "Test results"; 112 sb.append("<html>\n<head><title>" + title + "</title>") 113 .append(HtmlHelper.getCssString(".")) 114 .append("</head><body>\n") 115 .append("<h2><p align='center'>").append(title).append("</p></h2>\n") 116 .append("<table border='1' width='100%' class='main-page'>") 117 .append("<tr><th>Suite</th><th>Passed</th><th>Failed</th><th>Skipped</th><th>testng.xml</th></tr>\n"); 118 119 int totalFailedTests = 0; 120 int totalPassedTests = 0; 121 int totalSkippedTests = 0; 122 123 StringBuffer suiteBuf= new StringBuffer(); 124 for (ISuite suite : suites) { 125 if (suite.getResults().size() == 0) { 126 continue; 127 } 128 129 String name = suite.getName(); 130 131 int failedTests= 0; 132 int passedTests= 0; 133 int skippedTests= 0; 134 135 Map<String, ISuiteResult> results = suite.getResults(); 136 for (ISuiteResult result : results.values()) { 137 ITestContext context = result.getTestContext(); 138 failedTests += context.getFailedTests().size(); 139 totalFailedTests += context.getFailedTests().size(); 140 passedTests += context.getPassedTests().size(); 141 totalPassedTests += context.getPassedTests().size(); 142 skippedTests += context.getSkippedTests().size(); 143 totalSkippedTests += context.getSkippedTests().size(); 144 } 145 146 String cls = failedTests > 0 ? "invocation-failed" 147 : (passedTests > 0 ? "invocation-passed" : "invocation-failed"); 148 suiteBuf.append("<tr align='center' class='").append(cls).append("'>") 149 .append("<td><a href='").append(name).append("/index.html'>") 150 .append(name).append("</a></td>\n"); 151 suiteBuf.append("<td>" + passedTests + "</td>") 152 .append("<td>" + failedTests + "</td>") 153 .append("<td>" + skippedTests + "</td>") 154 .append("<td><a href='").append(name).append("/").append(TESTNG_XML).append("'>Link").append("</a></td>") 155 .append("</tr>"); 156 157 } 158 159 String cls= totalFailedTests > 0 ? "invocation-failed" 160 : (totalPassedTests > 0 ? "invocation-passed" : "invocation-failed"); 161 sb.append("<tr align='center' class='").append(cls).append("'>") 162 .append("<td><em>Total</em></td>") 163 .append("<td><em>").append(totalPassedTests).append("</em></td>") 164 .append("<td><em>").append(totalFailedTests).append("</em></td>") 165 .append("<td><em>").append(totalSkippedTests).append("</em></td>") 166 .append("<td> </td>") 167 .append("</tr>\n"); 168 sb.append(suiteBuf); 169 sb.append("</table>").append("</body></html>\n"); 170 171 Utils.writeFile(m_outputDirectory, "index.html", sb.toString()); 172 } 173 generateExcludedMethodsReport(XmlSuite xmlSuite, ISuite suite)174 private void generateExcludedMethodsReport(XmlSuite xmlSuite, ISuite suite) { 175 Collection<ITestNGMethod> excluded = suite.getExcludedMethods(); 176 StringBuffer sb2 = new StringBuffer("<h2>Methods that were not run</h2><table>\n"); 177 for (ITestNGMethod method : excluded) { 178 Method m = method.getMethod(); 179 if (m != null) { 180 sb2.append("<tr><td>") 181 .append(m.getDeclaringClass().getName() + "." + m.getName()); 182 String description = method.getDescription(); 183 if(isStringNotEmpty(description)) { 184 sb2.append("<br/>").append(SP2).append("<i>").append(description).append("</i>"); 185 } 186 sb2.append("</td></tr>\n"); 187 } 188 } 189 sb2.append("</table>"); 190 191 Utils.writeFile(getOutputDirectory(xmlSuite), METHODS_NOT_RUN, sb2.toString()); 192 } 193 generateReporterOutput(XmlSuite xmlSuite, ISuite suite)194 private void generateReporterOutput(XmlSuite xmlSuite, ISuite suite) { 195 StringBuffer sb = new StringBuffer(); 196 197 // 198 // Reporter output 199 // 200 sb.append("<h2>Reporter output</h2>") 201 .append("<table>"); 202 List<String> output = Reporter.getOutput(); 203 for (String line : output) { 204 sb.append("<tr><td>").append(line).append("</td></tr>\n"); 205 } 206 207 sb.append("</table>"); 208 209 Utils.writeFile(getOutputDirectory(xmlSuite), REPORTER_OUTPUT, sb.toString()); 210 } 211 generateClasses(XmlSuite xmlSuite, ISuite suite)212 private void generateClasses(XmlSuite xmlSuite, ISuite suite) { 213 StringBuffer sb = new StringBuffer(); 214 sb.append("<table border='1'>\n") 215 .append("<tr>\n") 216 .append("<th>Class name</th>\n") 217 .append("<th>Method name</th>\n") 218 .append("<th>Groups</th>\n") 219 .append("</tr>") 220 ; 221 for (ITestClass tc : m_classes.values()) { 222 sb.append(generateClass(tc)); 223 } 224 225 sb.append("</table>\n"); 226 227 Utils.writeFile(getOutputDirectory(xmlSuite), CLASSES, sb.toString()); 228 } 229 230 private final static String SP = " "; 231 private final static String SP2 = SP + SP + SP + SP; 232 private final static String SP3 = SP2 + SP2; 233 private final static String SP4 = SP3 + SP3; 234 generateClass(ITestClass cls)235 private String generateClass(ITestClass cls) { 236 StringBuffer sb = new StringBuffer(); 237 238 sb.append("<tr>\n") 239 .append("<td>").append(cls.getRealClass().getName()).append("</td>\n") 240 .append("<td> </td>") 241 .append("<td> </td>") 242 .append("</tr>\n") 243 ; 244 245 String[] tags = new String[] { 246 "@Test", 247 "@BeforeClass", 248 "@BeforeMethod", 249 "@AfterMethod", 250 "@AfterClass" 251 }; 252 ITestNGMethod[][] methods = new ITestNGMethod[][] { 253 cls.getTestMethods(), 254 cls.getBeforeClassMethods(), 255 cls.getBeforeTestMethods(), 256 cls.getAfterTestMethods(), 257 cls.getAfterClassMethods() 258 }; 259 260 for (int i = 0; i < tags.length; i++) { 261 sb.append("<tr>\n") 262 .append("<td align='center' colspan='3'>").append(tags[i]).append("</td>\n") 263 .append("</tr>\n") 264 .append(dumpMethods(methods[i])) 265 ; 266 } 267 // sb.append("<hr width='100%'/>") 268 // .append("<h3>").append(cls.getRealClass().getName()).append("</h3>\n"); 269 // 270 // sb.append("<div>").append(SP3).append("Test methods\n") 271 // .append(dumpMethods(cls.getTestMethods())).append("</div>\n") 272 // .append("<div>").append(SP3).append("@BeforeClass\n") 273 // .append(dumpMethods(cls.getBeforeClassMethods())).append("</div>\n") 274 // .append("<div>").append(SP3).append("@BeforeMethod\n") 275 // .append(dumpMethods(cls.getBeforeTestMethods())).append("</div>\n") 276 // .append("<div>").append(SP3).append("@AfterMethod\n") 277 // .append(dumpMethods(cls.getAfterTestMethods())).append("</div>\n") 278 // .append("<div>").append(SP3).append("@AfterClass\n") 279 // .append(dumpMethods(cls.getAfterClassMethods())).append("</div>\n") 280 // ; 281 282 String result = sb.toString(); 283 return result; 284 } 285 dumpMethods(ITestNGMethod[] testMethods)286 private String dumpMethods(ITestNGMethod[] testMethods) { 287 StringBuffer sb = new StringBuffer(); 288 if(null == testMethods || testMethods.length == 0) { 289 return ""; 290 } 291 292 for (ITestNGMethod m : testMethods) { 293 sb.append("<tr>\n"); 294 sb.append("<td> </td>\n") 295 .append("<td>").append(m.getMethodName()).append("</td>\n") 296 ; 297 String[] groups = m.getGroups(); 298 if (groups != null && groups.length > 0) { 299 sb.append("<td>"); 300 for (String g : groups) { 301 sb.append(g).append(" "); 302 } 303 sb.append("</td>\n"); 304 } 305 else { 306 sb.append("<td> </td>"); 307 } 308 sb.append("</tr>\n"); 309 } 310 311 // StringBuffer sb = new StringBuffer("<br/>"); //"<table bgcolor=\"#c0c0c0\"/>"); 312 // for (ITestNGMethod tm : testMethods) { 313 // sb 314 // .append(SP4).append(tm.getMethodName()).append("()\n") 315 // .append(dumpGroups(tm.getGroups())) 316 // .append("<br/>"); 317 // ; 318 // } 319 320 321 String result = sb.toString(); 322 return result; 323 } 324 dumpGroups(String[] groups)325 private String dumpGroups(String[] groups) { 326 StringBuffer sb = new StringBuffer(); 327 328 if (null != groups && groups.length > 0) { 329 sb.append(SP4).append("<em>["); 330 331 for (String g : groups) { 332 sb.append(g).append(" "); 333 } 334 335 sb.append("]</em><br/>\n"); 336 } 337 338 String result = sb.toString(); 339 return result; 340 } 341 342 /** 343 * Generate information about the methods that were run 344 */ 345 public static final String AFTER= "<<"; 346 public static final String BEFORE = ">>"; generateMethodsChronologically(XmlSuite xmlSuite, ISuite suite, String outputFileName, boolean alphabetical)347 private void generateMethodsChronologically(XmlSuite xmlSuite, ISuite suite, 348 String outputFileName, boolean alphabetical) 349 { 350 try (BufferedWriter bw = Utils.openWriter(getOutputDirectory(xmlSuite), outputFileName)) { 351 bw.append("<h2>Methods run, sorted chronologically</h2>"); 352 bw.append("<h3>" + BEFORE + " means before, " + AFTER + " means after</h3><p/>"); 353 354 long startDate = -1; 355 bw.append("<br/><em>").append(suite.getName()).append("</em><p/>"); 356 bw.append("<small><i>(Hover the method name to see the test class name)</i></small><p/>\n"); 357 358 Collection<IInvokedMethod> invokedMethods = suite.getAllInvokedMethods(); 359 if (alphabetical) { 360 @SuppressWarnings({"unchecked"}) 361 Comparator<? super ITestNGMethod> alphabeticalComparator = new Comparator(){ 362 @Override 363 public int compare(Object o1, Object o2) { 364 IInvokedMethod m1 = (IInvokedMethod) o1; 365 IInvokedMethod m2 = (IInvokedMethod) o2; 366 return m1.getTestMethod().getMethodName().compareTo(m2.getTestMethod().getMethodName()); 367 } 368 }; 369 Collections.sort((List) invokedMethods, alphabeticalComparator); 370 } 371 372 SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss"); 373 boolean addedHeader = false; 374 for (IInvokedMethod iim : invokedMethods) { 375 ITestNGMethod tm = iim.getTestMethod(); 376 if (!addedHeader) { 377 bw.append("<table border=\"1\">\n") 378 .append("<tr>") 379 .append("<th>Time</th>") 380 .append("<th>Delta (ms)</th>") 381 .append("<th>Suite<br>configuration</th>") 382 .append("<th>Test<br>configuration</th>") 383 .append("<th>Class<br>configuration</th>") 384 .append("<th>Groups<br>configuration</th>") 385 .append("<th>Method<br>configuration</th>") 386 .append("<th>Test<br>method</th>") 387 .append("<th>Thread</th>") 388 .append("<th>Instances</th>") 389 .append("</tr>\n"); 390 addedHeader = true; 391 } 392 String methodName = tm.toString(); 393 boolean bc = tm.isBeforeClassConfiguration(); 394 boolean ac = tm.isAfterClassConfiguration(); 395 boolean bt = tm.isBeforeTestConfiguration(); 396 boolean at = tm.isAfterTestConfiguration(); 397 boolean bs = tm.isBeforeSuiteConfiguration(); 398 boolean as = tm.isAfterSuiteConfiguration(); 399 boolean bg = tm.isBeforeGroupsConfiguration(); 400 boolean ag = tm.isAfterGroupsConfiguration(); 401 boolean setUp = tm.isBeforeMethodConfiguration(); 402 boolean tearDown = tm.isAfterMethodConfiguration(); 403 boolean isClassConfiguration = bc || ac; 404 boolean isGroupsConfiguration = bg || ag; 405 boolean isTestConfiguration = bt || at; 406 boolean isSuiteConfiguration = bs || as; 407 boolean isSetupOrTearDown = setUp || tearDown; 408 String configurationClassMethod = isClassConfiguration ? (bc ? BEFORE : AFTER) + methodName : SP; 409 String configurationTestMethod = isTestConfiguration ? (bt ? BEFORE : AFTER) + methodName : SP; 410 String configurationGroupsMethod = isGroupsConfiguration ? (bg ? BEFORE : AFTER) + methodName : SP; 411 String configurationSuiteMethod = isSuiteConfiguration ? (bs ? BEFORE : AFTER) + methodName : SP; 412 String setUpOrTearDownMethod = isSetupOrTearDown ? (setUp ? BEFORE : AFTER) + methodName : SP; 413 String testMethod = tm.isTest() ? methodName : SP; 414 415 StringBuffer instances = new StringBuffer(); 416 for (long o : tm.getInstanceHashCodes()) { 417 instances.append(o).append(" "); 418 } 419 420 if (startDate == -1) { 421 startDate = iim.getDate(); 422 } 423 String date = format.format(iim.getDate()); 424 bw.append("<tr bgcolor=\"" + createColor(tm) + "\">") 425 .append(" <td>").append(date).append("</td> ") 426 .append(" <td>").append(Long.toString(iim.getDate() - startDate)).append("</td> ") 427 .append(td(configurationSuiteMethod)) 428 .append(td(configurationTestMethod)) 429 .append(td(configurationClassMethod)) 430 .append(td(configurationGroupsMethod)) 431 .append(td(setUpOrTearDownMethod)) 432 .append(td(testMethod)) 433 .append(" <td>").append(tm.getId()).append("</td> ") 434 .append(" <td>").append(instances).append("</td> ") 435 .append("</tr>\n") 436 ; 437 } 438 bw.append("</table>\n"); 439 } catch (IOException e) { 440 Utils.log("[SuiteHTMLReporter]", 1, "Error writing to " + outputFileName + ": " + e.getMessage()); 441 } 442 } 443 444 /** 445 * Generate a HTML color based on the class of the method 446 */ createColor(ITestNGMethod tm)447 private String createColor(ITestNGMethod tm) { 448 // real class can be null if this client is remote (not serializable) 449 long color = tm.getRealClass() != null ? tm.getRealClass().hashCode() & 0xffffff: 0xffffff; 450 long[] rgb = { 451 ((color & 0xff0000) >> 16) & 0xff, 452 ((color & 0x00ff00) >> 8) & 0xff, 453 color & 0xff 454 }; 455 // Not too dark 456 for (int i = 0; i < rgb.length; i++) { 457 if (rgb[i] < 0x60) { 458 rgb[i] += 0x60; 459 } 460 } 461 long adjustedColor = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; 462 String result = Long.toHexString(adjustedColor); 463 464 return result; 465 } 466 td(String s)467 private String td(String s) { 468 StringBuffer result = new StringBuffer(); 469 String prefix = ""; 470 471 if (s.startsWith(BEFORE)) { 472 prefix = BEFORE; 473 } 474 else if (s.startsWith(AFTER)) { 475 prefix = AFTER; 476 } 477 478 if (! s.equals(SP)) { 479 result.append("<td title=\"").append(s).append("\">"); 480 int open = s.lastIndexOf("("); 481 int start = s.substring(0, open).lastIndexOf("."); 482 // int end = s.lastIndexOf(")"); 483 if (start >= 0) { 484 result.append(prefix + s.substring(start + 1, open)); 485 } 486 else { 487 result.append(prefix + s); 488 } 489 result.append("</td> \n"); 490 } 491 else { 492 result.append("<td>").append(SP).append("</td>"); 493 } 494 495 return result.toString(); 496 } 497 ppp(String s)498 private void ppp(String s) { 499 System.out.println("[SuiteHTMLReporter] " + s); 500 } 501 502 /** 503 * Generate information about methods and groups 504 */ generateMethodsAndGroups(XmlSuite xmlSuite, ISuite suite)505 private void generateMethodsAndGroups(XmlSuite xmlSuite, ISuite suite) { 506 StringBuffer sb = new StringBuffer(); 507 508 Map<String, Collection<ITestNGMethod>> groups = suite.getMethodsByGroups(); 509 510 sb.append("<h2>Groups used for this test run</h2>"); 511 if (groups.size() > 0) { 512 sb.append("<table border=\"1\">\n") 513 .append("<tr> <td align=\"center\"><b>Group name</b></td>") 514 .append("<td align=\"center\"><b>Methods</b></td></tr>"); 515 516 String[] groupNames = groups.keySet().toArray(new String[groups.size()]); 517 Arrays.sort(groupNames); 518 for (String group : groupNames) { 519 Collection<ITestNGMethod> methods = groups.get(group); 520 sb.append("<tr><td>").append(group).append("</td>"); 521 StringBuffer methodNames = new StringBuffer(); 522 Map<ITestNGMethod, ITestNGMethod> uniqueMethods = Maps.newHashMap(); 523 for (ITestNGMethod tm : methods) { 524 uniqueMethods.put(tm, tm); 525 } 526 for (ITestNGMethod tm : uniqueMethods.values()) { 527 methodNames.append(tm.toString()).append("<br/>"); 528 } 529 sb.append("<td>" + methodNames.toString() + "</td></tr>\n"); 530 } 531 532 sb.append("</table>\n"); 533 } 534 Utils.writeFile(getOutputDirectory(xmlSuite), GROUPS, sb.toString()); 535 } 536 generateIndex(XmlSuite xmlSuite, ISuite sr)537 private void generateIndex(XmlSuite xmlSuite, ISuite sr) { 538 StringBuffer index = new StringBuffer() 539 .append("<html><head><title>Results for " + sr.getName() + "</title></head>\n") 540 .append("<frameset cols=\"26%,74%\">\n") 541 .append("<frame src=\"toc.html\" name=\"navFrame\">\n") 542 .append("<frame src=\"main.html\" name=\"mainFrame\">\n") 543 .append("</frameset>\n") 544 .append("</html>\n") 545 ; 546 547 Utils.writeFile(getOutputDirectory(xmlSuite), "index.html", index.toString()); 548 } 549 makeTitle(ISuite suite)550 private String makeTitle(ISuite suite) { 551 return "Results for<br/><em>" + suite.getName() + "</em>"; 552 } 553 generateMain(XmlSuite xmlSuite, ISuite sr)554 private void generateMain(XmlSuite xmlSuite, ISuite sr) { 555 StringBuffer index = new StringBuffer() 556 .append("<html><head><title>Results for " + sr.getName() + "</title></head>\n") 557 .append("<body>Select a result on the left-hand pane.</body>") 558 .append("</html>\n") 559 ; 560 561 Utils.writeFile(getOutputDirectory(xmlSuite), "main.html", index.toString()); 562 } 563 564 /** 565 * 566 */ generateTableOfContents(XmlSuite xmlSuite, ISuite suite)567 private void generateTableOfContents(XmlSuite xmlSuite, ISuite suite) { 568 StringBuffer tableOfContents = new StringBuffer(); 569 570 // 571 // Generate methods and groups hyperlinks 572 // 573 Map<String, ISuiteResult> suiteResults = suite.getResults(); 574 int groupCount = suite.getMethodsByGroups().size(); 575 int methodCount = 0; 576 for (ISuiteResult sr : suiteResults.values()) { 577 ITestNGMethod[] methods = sr.getTestContext().getAllTestMethods(); 578 methodCount += Utils.calculateInvokedMethodCount(methods); 579 580 // Collect testClasses 581 for (ITestNGMethod tm : methods) { 582 ITestClass tc = tm.getTestClass(); 583 m_classes.put(tc.getRealClass().getName(), tc); 584 } 585 } 586 587 String name = "Results for " + suite.getName(); 588 tableOfContents 589 .append("<html>\n") 590 .append("<head>\n") 591 .append("<title>" + name + "</title>\n") 592 .append(HtmlHelper.getCssString()) 593 .append("</head>\n") 594 ; 595 tableOfContents 596 .append("<body>\n") 597 .append("<h3><p align=\"center\">" + makeTitle(suite) + "</p></h3>\n") 598 .append("<table border='1' width='100%'>\n") 599 .append("<tr valign='top'>\n") 600 .append("<td>") 601 .append(suiteResults.size()).append(" ").append(pluralize(suiteResults.size(), "test")) 602 .append("</td>\n") 603 .append("<td>") 604 .append("<a target='mainFrame' href='").append(CLASSES).append("'>") 605 .append(m_classes.size() + " " + pluralize(m_classes.size(), "class")) 606 .append("</a>") 607 .append("</td>\n") 608 .append("<td>" + methodCount + " " + pluralize(methodCount, "method") + ":<br/>\n") 609 .append(" <a target='mainFrame' href='").append(METHODS_CHRONOLOGICAL).append("'>").append("chronological</a><br/>\n") 610 .append(" <a target='mainFrame' href='").append(METHODS_ALPHABETICAL).append("\'>").append("alphabetical</a><br/>\n") 611 .append(" <a target='mainFrame' href='").append(METHODS_NOT_RUN).append("'>not run (" + suite.getExcludedMethods().size() + ")</a>") 612 .append("</td>\n") 613 .append("</tr>\n") 614 615 .append("<tr>\n") 616 .append("<td><a target='mainFrame' href='").append(GROUPS).append("'>").append(groupCount + pluralize(groupCount, " group") + "</a></td>\n") 617 .append("<td><a target='mainFrame' href='").append(REPORTER_OUTPUT).append("'>reporter output</a></td>\n") 618 .append("<td><a target='mainFrame' href='").append(TESTNG_XML).append("'>testng.xml</a></td>\n") 619 .append("</tr>") 620 .append("</table>"); 621 622 // 623 // Generate results for individual tests 624 // 625 626 // Order the results so we can show the failures first, then the skip and 627 // finally the successes 628 Map<String, ISuiteResult> redResults = Maps.newHashMap(); 629 Map<String, ISuiteResult> yellowResults = Maps.newHashMap(); 630 Map<String, ISuiteResult> greenResults = Maps.newHashMap(); 631 632 for (Map.Entry<String, ISuiteResult> entry : suiteResults.entrySet()) { 633 String suiteName = entry.getKey(); 634 ISuiteResult sr = entry.getValue(); 635 ITestContext tc = sr.getTestContext(); 636 int failed = tc.getFailedTests().size(); 637 int skipped = tc.getSkippedTests().size(); 638 int passed = tc.getPassedTests().size(); 639 640 if (failed > 0) { 641 redResults.put(suiteName, sr); 642 } 643 else if (skipped > 0) { 644 yellowResults.put(suiteName, sr); 645 } 646 else if (passed > 0) { 647 greenResults.put(suiteName, sr); 648 } 649 else { 650 redResults.put(suiteName, sr); 651 } 652 } 653 654 655 ISuiteResult[][] results = new ISuiteResult[][] { 656 sortResults(redResults.values()), sortResults(yellowResults.values()), sortResults(greenResults.values()) 657 }; 658 659 String[] colors = {"failed", "skipped", "passed"}; 660 for (int i = 0; i < colors.length; i++) { 661 ISuiteResult[] r = results[i]; 662 for (ISuiteResult sr: r) { 663 String suiteName = sr.getTestContext().getName(); 664 generateSuiteResult(suiteName, sr, colors[i], tableOfContents, m_outputDirectory); 665 } 666 } 667 668 tableOfContents.append("</body></html>"); 669 Utils.writeFile(getOutputDirectory(xmlSuite), "toc.html", tableOfContents.toString()); 670 } 671 pluralize(int count, String singular)672 private String pluralize(int count, String singular) { 673 return count > 1 ? (singular.endsWith("s") ? singular + "es" : singular + "s") : singular; 674 } 675 getOutputDirectory(XmlSuite xmlSuite)676 private String getOutputDirectory(XmlSuite xmlSuite) { 677 return m_outputDirectory + File.separatorChar + xmlSuite.getName(); 678 } 679 sortResults(Collection<ISuiteResult> r)680 private ISuiteResult[] sortResults(Collection<ISuiteResult> r) { 681 ISuiteResult[] result = r.toArray(new ISuiteResult[r.size()]); 682 Arrays.sort(result); 683 return result; 684 } 685 generateSuiteResult(String suiteName, ISuiteResult sr, String cssClass, StringBuffer tableOfContents, String outputDirectory)686 private void generateSuiteResult(String suiteName, 687 ISuiteResult sr, 688 String cssClass, 689 StringBuffer tableOfContents, 690 String outputDirectory) 691 { 692 ITestContext tc = sr.getTestContext(); 693 int passed = tc.getPassedTests().size(); 694 int failed = tc.getFailedTests().size(); 695 int skipped = tc.getSkippedTests().size(); 696 String baseFile = tc.getName(); 697 tableOfContents 698 .append("\n<table width='100%' class='test-").append(cssClass).append("'>\n") 699 .append("<tr><td>\n") 700 .append("<table style='width: 100%'><tr>") 701 .append("<td valign='top'>") 702 .append(suiteName).append(" (").append(passed).append("/").append(failed).append("/").append(skipped).append(")") 703 .append("</td>") 704 .append("<td valign='top' align='right'>\n") 705 .append(" <a href='" + baseFile + ".html' target='mainFrame'>Results</a>\n") 706 // .append(" <a href=\"" + baseFile + ".out\" target=\"mainFrame\"\">Output</a>\n") 707 // .append(" <a href=\"file://" + baseFile + ".properties\" target=\"mainFrame\"\">Property file</a><br>\n") 708 .append("</td>") 709 .append("</tr></table>\n") 710 .append("</td></tr><p/>\n") 711 ; 712 713 tableOfContents.append("</table>\n"); 714 } 715 716 /** 717 * Writes a property file for each suite result. 718 * 719 * @param xmlSuite 720 * @param suite 721 */ generateSuites(XmlSuite xmlSuite, ISuite suite)722 private void generateSuites(XmlSuite xmlSuite, ISuite suite) { 723 Map<String, ISuiteResult> suiteResults = suite.getResults(); 724 725 for (ISuiteResult sr : suiteResults.values()) { 726 ITestContext testContext = sr.getTestContext(); 727 StringBuffer sb = new StringBuffer(); 728 729 for (ISuiteResult suiteResult : suiteResults.values()) { 730 sb.append(suiteResult.toString()); 731 } 732 Utils.writeFile(getOutputDirectory(xmlSuite), testContext.getName() + ".properties", sb.toString()); 733 } 734 } 735 } 736