blob: 953b11781a232c9ec38250664d55ae34d2f4f33e [file] [log] [blame]
package org.testng.reporters;
import java.lang.reflect.Method;
import java.util.List;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import org.testng.internal.Utils;
/**
* Reporter printing out detailed messages about what TestNG
* is going to run and what is the status of what has been just run.
*
* To see messages from this reporter, either run Ant in verbose mode ('ant -v')
* or set verbose level to 5 or higher
*
* @author Lukas Jungmann
* @since 6.4
*/
public class VerboseReporter extends TestListenerAdapter {
/**
* Default prefix for messages printed out by this reporter
*
*/
public static final String LISTENER_PREFIX = "[VerboseTestNG] ";
private String suiteName;
private final String prefix;
private enum Status {
SUCCESS(ITestResult.SUCCESS), FAILURE(ITestResult.FAILURE), SKIP(ITestResult.SKIP),
SUCCESS_PERCENTAGE_FAILURE(ITestResult.SUCCESS_PERCENTAGE_FAILURE), STARTED(ITestResult.STARTED);
private int status;
private Status(int i) {
status = i;
}
}
/**
* Default constructor
*/
public VerboseReporter() {
this(LISTENER_PREFIX);
}
/**
* Create VerboseReporter with custom prefix
*
* @param prefix prefix for messages printed out by this reporter
*/
public VerboseReporter(String prefix) {
this.prefix = prefix;
}
@Override
public void beforeConfiguration(ITestResult tr) {
super.beforeConfiguration(tr);
logTestResult(Status.STARTED, tr, true);
}
@Override
public void onConfigurationFailure(ITestResult tr) {
super.onConfigurationFailure(tr);
logTestResult(Status.FAILURE, tr, true);
}
@Override
public void onConfigurationSkip(ITestResult tr) {
super.onConfigurationSkip(tr);
logTestResult(Status.SKIP, tr, true);
}
@Override
public void onConfigurationSuccess(ITestResult tr) {
super.onConfigurationSuccess(tr);
logTestResult(Status.SUCCESS, tr, true);
}
@Override
public void onTestStart(ITestResult tr) {
logTestResult(Status.STARTED, tr, false);
}
@Override
public void onTestFailure(ITestResult tr) {
super.onTestFailure(tr);
logTestResult(Status.FAILURE, tr, false);
}
@Override
public void onTestFailedButWithinSuccessPercentage(ITestResult tr) {
super.onTestFailedButWithinSuccessPercentage(tr);
logTestResult(Status.SUCCESS_PERCENTAGE_FAILURE, tr, false);
}
@Override
public void onTestSkipped(ITestResult tr) {
super.onTestSkipped(tr);
logTestResult(Status.SKIP, tr, false);
}
@Override
public void onTestSuccess(ITestResult tr) {
super.onTestSuccess(tr);
logTestResult(Status.SUCCESS, tr, false);
}
@Override
public void onStart(ITestContext ctx) {
suiteName = ctx.getName();//ctx.getSuite().getXmlSuite().getFileName();
log("RUNNING: Suite: \"" + suiteName + "\" containing \"" + ctx.getAllTestMethods().length + "\" Tests (config: " + ctx.getSuite().getXmlSuite().getFileName() + ")");
}
@Override
public void onFinish(ITestContext context) {
logResults();
suiteName = null;
}
private ITestNGMethod[] resultsToMethods(List<ITestResult> results) {
ITestNGMethod[] result = new ITestNGMethod[results.size()];
int i = 0;
for (ITestResult tr : results) {
result[i++] = tr.getMethod();
}
return result;
}
/**
* Print out test summary
*/
private void logResults() {
//
// Log test summary
//
ITestNGMethod[] ft = resultsToMethods(getFailedTests());
StringBuilder sb = new StringBuilder("\n===============================================\n");
sb.append(" ").append(suiteName).append("\n");
sb.append(" Tests run: ").append(Utils.calculateInvokedMethodCount(getAllTestMethods()));
sb.append(", Failures: ").append(Utils.calculateInvokedMethodCount(ft));
sb.append(", Skips: ").append(Utils.calculateInvokedMethodCount(resultsToMethods(getSkippedTests())));
int confFailures = getConfigurationFailures().size();
int confSkips = getConfigurationSkips().size();
if (confFailures > 0 || confSkips > 0) {
sb.append("\n").append(" Configuration Failures: ").append(confFailures);
sb.append(", Skips: ").append(confSkips);
}
sb.append("\n===============================================");
log(sb.toString());
}
/**
* Log meaningful message for passed in arguments.
* Message itself is of form:
* $status: "$suiteName" - $methodDeclaration ($actualArguments) finished in $x ms
*
* @param st status of passed in itr
* @param itr test result to be described
* @param isConfMethod is itr describing configuration method
*/
private void logTestResult(Status st, ITestResult itr, boolean isConfMethod) {
StringBuilder sb = new StringBuilder();
String stackTrace = "";
switch (st) {
case STARTED:
sb.append("INVOKING");
break;
case SKIP:
sb.append("SKIPPED");
stackTrace = itr.getThrowable() != null
? Utils.stackTrace(itr.getThrowable(), false)[0] : "";
break;
case FAILURE:
sb.append("FAILED");
stackTrace = itr.getThrowable() != null
? Utils.stackTrace(itr.getThrowable(), false)[0] : "";
break;
case SUCCESS:
sb.append("PASSED");
break;
case SUCCESS_PERCENTAGE_FAILURE:
sb.append("PASSED with failures");
break;
default:
//not happen
throw new RuntimeException("Unsupported test status:" + itr.getStatus());
}
if (isConfMethod) {
sb.append(" CONFIGURATION: ");
} else {
sb.append(": ");
}
ITestNGMethod tm = itr.getMethod();
int identLevel = sb.length();
sb.append(getMethodDeclaration(tm));
Object[] params = itr.getParameters();
Class[] paramTypes = tm.getMethod().getParameterTypes();
if (null != params && params.length > 0) {
// The error might be a data provider parameter mismatch, so make
// a special case here
if (params.length != paramTypes.length) {
sb.append("Wrong number of arguments were passed by the Data Provider: found ");
sb.append(params.length);
sb.append(" but expected ");
sb.append(paramTypes.length);
} else {
sb.append("(value(s): ");
for (int i = 0; i < params.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(Utils.toString(params[i], paramTypes[i]));
}
sb.append(")");
}
}
if (Status.STARTED != st) {
sb.append(" finished in ");
sb.append(itr.getEndMillis() - itr.getStartMillis());
sb.append(" ms");
if (!Utils.isStringEmpty(tm.getDescription())) {
sb.append("\n");
for (int i = 0; i < identLevel; i++) {
sb.append(" ");
}
sb.append(tm.getDescription());
}
if (!Utils.isStringEmpty(stackTrace)) {
sb.append("\n").append(stackTrace.substring(0, stackTrace.lastIndexOf(System.getProperty("line.separator"))));
}
}
log(sb.toString());
}
private void log(String message) {
//prefix all output lines
System.out.println(message.replaceAll("(?m)^", prefix));
}
/**
*
* @param method method to be described
* @return FQN of a class + method declaration for a method passed in
* ie. test.triangle.CheckCount.testCheckCount(java.lang.String)
*/
private String getMethodDeclaration(ITestNGMethod method) {
//see Utils.detailedMethodName
//perhaps should rather adopt the original method instead
Method m = method.getMethod();
StringBuilder buf = new StringBuilder();
buf.append("\"");
if (suiteName != null) {
buf.append(suiteName);
} else {
buf.append("UNKNOWN");
}
buf.append("\"");
buf.append(" - ");
if (method.isBeforeSuiteConfiguration()) {
buf.append("@BeforeSuite ");
} else if (method.isBeforeTestConfiguration()) {
buf.append("@BeforeTest ");
} else if (method.isBeforeClassConfiguration()) {
buf.append("@BeforeClass ");
} else if (method.isBeforeGroupsConfiguration()) {
buf.append("@BeforeGroups ");
} else if (method.isBeforeMethodConfiguration()) {
buf.append("@BeforeMethod ");
} else if (method.isAfterMethodConfiguration()) {
buf.append("@AfterMethod ");
} else if (method.isAfterGroupsConfiguration()) {
buf.append("@AfterGroups ");
} else if (method.isAfterClassConfiguration()) {
buf.append("@AfterClass ");
} else if (method.isAfterTestConfiguration()) {
buf.append("@AfterTest ");
} else if (method.isAfterSuiteConfiguration()) {
buf.append("@AfterSuite ");
}
buf.append(m.getDeclaringClass().getName());
buf.append(".");
buf.append(m.getName());
buf.append("(");
int i = 0;
for (Class<?> p : m.getParameterTypes()) {
if (i++ > 0) {
buf.append(", ");
}
buf.append(p.getName());
}
buf.append(")");
return buf.toString();
}
@Override
public String toString() {
return "VerboseReporter{" + "suiteName=" + suiteName + '}';
}
}