1 package org.testng.reporters; 2 3 import java.lang.reflect.Method; 4 import java.util.List; 5 import org.testng.ITestContext; 6 import org.testng.ITestNGMethod; 7 import org.testng.ITestResult; 8 import org.testng.TestListenerAdapter; 9 import org.testng.internal.Utils; 10 11 /** 12 * Reporter printing out detailed messages about what TestNG 13 * is going to run and what is the status of what has been just run. 14 * 15 * To see messages from this reporter, either run Ant in verbose mode ('ant -v') 16 * or set verbose level to 5 or higher 17 * 18 * @author Lukas Jungmann 19 * @since 6.4 20 */ 21 public class VerboseReporter extends TestListenerAdapter { 22 23 /** 24 * Default prefix for messages printed out by this reporter 25 * 26 */ 27 public static final String LISTENER_PREFIX = "[VerboseTestNG] "; 28 private String suiteName; 29 private final String prefix; 30 31 private enum Status { 32 33 SUCCESS(ITestResult.SUCCESS), FAILURE(ITestResult.FAILURE), SKIP(ITestResult.SKIP), 34 SUCCESS_PERCENTAGE_FAILURE(ITestResult.SUCCESS_PERCENTAGE_FAILURE), STARTED(ITestResult.STARTED); 35 private int status; 36 Status(int i)37 private Status(int i) { 38 status = i; 39 } 40 } 41 42 /** 43 * Default constructor 44 */ VerboseReporter()45 public VerboseReporter() { 46 this(LISTENER_PREFIX); 47 } 48 49 /** 50 * Create VerboseReporter with custom prefix 51 * 52 * @param prefix prefix for messages printed out by this reporter 53 */ VerboseReporter(String prefix)54 public VerboseReporter(String prefix) { 55 this.prefix = prefix; 56 } 57 58 @Override beforeConfiguration(ITestResult tr)59 public void beforeConfiguration(ITestResult tr) { 60 super.beforeConfiguration(tr); 61 logTestResult(Status.STARTED, tr, true); 62 } 63 64 @Override onConfigurationFailure(ITestResult tr)65 public void onConfigurationFailure(ITestResult tr) { 66 super.onConfigurationFailure(tr); 67 logTestResult(Status.FAILURE, tr, true); 68 } 69 70 @Override onConfigurationSkip(ITestResult tr)71 public void onConfigurationSkip(ITestResult tr) { 72 super.onConfigurationSkip(tr); 73 logTestResult(Status.SKIP, tr, true); 74 } 75 76 @Override onConfigurationSuccess(ITestResult tr)77 public void onConfigurationSuccess(ITestResult tr) { 78 super.onConfigurationSuccess(tr); 79 logTestResult(Status.SUCCESS, tr, true); 80 } 81 82 @Override onTestStart(ITestResult tr)83 public void onTestStart(ITestResult tr) { 84 logTestResult(Status.STARTED, tr, false); 85 } 86 87 @Override onTestFailure(ITestResult tr)88 public void onTestFailure(ITestResult tr) { 89 super.onTestFailure(tr); 90 logTestResult(Status.FAILURE, tr, false); 91 } 92 93 @Override onTestFailedButWithinSuccessPercentage(ITestResult tr)94 public void onTestFailedButWithinSuccessPercentage(ITestResult tr) { 95 super.onTestFailedButWithinSuccessPercentage(tr); 96 logTestResult(Status.SUCCESS_PERCENTAGE_FAILURE, tr, false); 97 } 98 99 @Override onTestSkipped(ITestResult tr)100 public void onTestSkipped(ITestResult tr) { 101 super.onTestSkipped(tr); 102 logTestResult(Status.SKIP, tr, false); 103 } 104 105 @Override onTestSuccess(ITestResult tr)106 public void onTestSuccess(ITestResult tr) { 107 super.onTestSuccess(tr); 108 logTestResult(Status.SUCCESS, tr, false); 109 } 110 111 @Override onStart(ITestContext ctx)112 public void onStart(ITestContext ctx) { 113 suiteName = ctx.getName();//ctx.getSuite().getXmlSuite().getFileName(); 114 log("RUNNING: Suite: \"" + suiteName + "\" containing \"" + ctx.getAllTestMethods().length + "\" Tests (config: " + ctx.getSuite().getXmlSuite().getFileName() + ")"); 115 } 116 117 @Override onFinish(ITestContext context)118 public void onFinish(ITestContext context) { 119 logResults(); 120 suiteName = null; 121 } 122 resultsToMethods(List<ITestResult> results)123 private ITestNGMethod[] resultsToMethods(List<ITestResult> results) { 124 ITestNGMethod[] result = new ITestNGMethod[results.size()]; 125 int i = 0; 126 for (ITestResult tr : results) { 127 result[i++] = tr.getMethod(); 128 } 129 return result; 130 } 131 132 /** 133 * Print out test summary 134 */ logResults()135 private void logResults() { 136 // 137 // Log test summary 138 // 139 ITestNGMethod[] ft = resultsToMethods(getFailedTests()); 140 StringBuilder sb = new StringBuilder("\n===============================================\n"); 141 sb.append(" ").append(suiteName).append("\n"); 142 sb.append(" Tests run: ").append(Utils.calculateInvokedMethodCount(getAllTestMethods())); 143 sb.append(", Failures: ").append(Utils.calculateInvokedMethodCount(ft)); 144 sb.append(", Skips: ").append(Utils.calculateInvokedMethodCount(resultsToMethods(getSkippedTests()))); 145 int confFailures = getConfigurationFailures().size(); 146 int confSkips = getConfigurationSkips().size(); 147 if (confFailures > 0 || confSkips > 0) { 148 sb.append("\n").append(" Configuration Failures: ").append(confFailures); 149 sb.append(", Skips: ").append(confSkips); 150 } 151 sb.append("\n==============================================="); 152 log(sb.toString()); 153 } 154 155 /** 156 * Log meaningful message for passed in arguments. 157 * Message itself is of form: 158 * $status: "$suiteName" - $methodDeclaration ($actualArguments) finished in $x ms ($run of $totalRuns) 159 * 160 * @param st status of passed in itr 161 * @param itr test result to be described 162 * @param isConfMethod is itr describing configuration method 163 */ logTestResult(Status st, ITestResult itr, boolean isConfMethod)164 private void logTestResult(Status st, ITestResult itr, boolean isConfMethod) { 165 StringBuilder sb = new StringBuilder(); 166 StringBuilder succRate = null; 167 String stackTrace = ""; 168 switch (st) { 169 case STARTED: 170 sb.append("INVOKING"); 171 break; 172 case SKIP: 173 sb.append("SKIPPED"); 174 stackTrace = itr.getThrowable() != null 175 ? Utils.stackTrace(itr.getThrowable(), false)[0] : ""; 176 break; 177 case FAILURE: 178 sb.append("FAILED"); 179 stackTrace = itr.getThrowable() != null 180 ? Utils.stackTrace(itr.getThrowable(), false)[0] : ""; 181 break; 182 case SUCCESS: 183 sb.append("PASSED"); 184 break; 185 case SUCCESS_PERCENTAGE_FAILURE: 186 sb.append("PASSED with failures"); 187 break; 188 default: 189 //not happen 190 throw new RuntimeException("Unsupported test status:" + itr.getStatus()); 191 } 192 if (isConfMethod) { 193 sb.append(" CONFIGURATION: "); 194 } else { 195 sb.append(": "); 196 } 197 ITestNGMethod tm = itr.getMethod(); 198 int identLevel = sb.length(); 199 sb.append(getMethodDeclaration(tm)); 200 Object[] params = itr.getParameters(); 201 Class[] paramTypes = tm.getMethod().getParameterTypes(); 202 if (null != params && params.length > 0) { 203 // The error might be a data provider parameter mismatch, so make 204 // a special case here 205 if (params.length != paramTypes.length) { 206 sb.append("Wrong number of arguments were passed by the Data Provider: found "); 207 sb.append(params.length); 208 sb.append(" but expected "); 209 sb.append(paramTypes.length); 210 } else { 211 sb.append("(value(s): "); 212 for (int i = 0; i < params.length; i++) { 213 if (i > 0) { 214 sb.append(", "); 215 } 216 sb.append(Utils.toString(params[i], paramTypes[i])); 217 } 218 sb.append(")"); 219 220 } 221 } 222 if (Status.STARTED != st) { 223 sb.append(" finished in "); 224 sb.append(itr.getEndMillis() - itr.getStartMillis()); 225 sb.append(" ms"); 226 if (!Utils.isStringEmpty(tm.getDescription())) { 227 sb.append("\n"); 228 for (int i = 0; i < identLevel; i++) { 229 sb.append(" "); 230 } 231 sb.append(tm.getDescription()); 232 } 233 if (tm.getInvocationCount() > 1) { 234 sb.append(" ("); 235 sb.append(tm.getCurrentInvocationCount()); 236 sb.append(" of "); 237 sb.append(tm.getInvocationCount()); 238 sb.append(")"); 239 } 240 if (!Utils.isStringEmpty(stackTrace)) { 241 sb.append("\n").append(stackTrace.substring(0, stackTrace.lastIndexOf(System.getProperty("line.separator")))); 242 } 243 } else { 244 if (!isConfMethod && tm.getInvocationCount() > 1) { 245 sb.append(" success: "); 246 sb.append(tm.getSuccessPercentage()); 247 sb.append("%"); 248 } 249 } 250 log(sb.toString()); 251 } 252 log(String message)253 protected void log(String message) { 254 //prefix all output lines 255 System.out.println(message.replaceAll("(?m)^", prefix)); 256 } 257 258 /** 259 * 260 * @param method method to be described 261 * @return FQN of a class + method declaration for a method passed in 262 * ie. test.triangle.CheckCount.testCheckCount(java.lang.String) 263 */ getMethodDeclaration(ITestNGMethod method)264 private String getMethodDeclaration(ITestNGMethod method) { 265 //see Utils.detailedMethodName 266 //perhaps should rather adopt the original method instead 267 Method m = method.getMethod(); 268 StringBuilder buf = new StringBuilder(); 269 buf.append("\""); 270 if (suiteName != null) { 271 buf.append(suiteName); 272 } else { 273 buf.append("UNKNOWN"); 274 } 275 buf.append("\""); 276 buf.append(" - "); 277 if (method.isBeforeSuiteConfiguration()) { 278 buf.append("@BeforeSuite "); 279 } else if (method.isBeforeTestConfiguration()) { 280 buf.append("@BeforeTest "); 281 } else if (method.isBeforeClassConfiguration()) { 282 buf.append("@BeforeClass "); 283 } else if (method.isBeforeGroupsConfiguration()) { 284 buf.append("@BeforeGroups "); 285 } else if (method.isBeforeMethodConfiguration()) { 286 buf.append("@BeforeMethod "); 287 } else if (method.isAfterMethodConfiguration()) { 288 buf.append("@AfterMethod "); 289 } else if (method.isAfterGroupsConfiguration()) { 290 buf.append("@AfterGroups "); 291 } else if (method.isAfterClassConfiguration()) { 292 buf.append("@AfterClass "); 293 } else if (method.isAfterTestConfiguration()) { 294 buf.append("@AfterTest "); 295 } else if (method.isAfterSuiteConfiguration()) { 296 buf.append("@AfterSuite "); 297 } 298 buf.append(m.getDeclaringClass().getName()); 299 buf.append("."); 300 buf.append(m.getName()); 301 buf.append("("); 302 int i = 0; 303 for (Class<?> p : m.getParameterTypes()) { 304 if (i++ > 0) { 305 buf.append(", "); 306 } 307 buf.append(p.getName()); 308 } 309 buf.append(")"); 310 return buf.toString(); 311 } 312 313 @Override toString()314 public String toString() { 315 return "VerboseReporter{" + "suiteName=" + suiteName + '}'; 316 } 317 } 318