package org.infinispan.commons.test; import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.jboss.logging.Logger; import org.testng.IConfigurationListener2; import org.testng.ISuite; import org.testng.ISuiteListener; import org.testng.ITestContext; import org.testng.ITestListener; import org.testng.ITestResult; import org.testng.annotations.Test; /** * Logs TestNG test progress. */ public class TestNGTestListener implements ITestListener, IConfigurationListener2, ISuiteListener { private static final Logger log = Logger.getLogger(TestNGTestListener.class); private Set<Long> startupThreads; private boolean suiteRunning; @Override public void onTestStart(ITestResult result) { TestSuiteProgress.testStarted(testName(result)); } @Override public void onTestSuccess(ITestResult result) { TestSuiteProgress.testFinished(testName(result)); } @Override public void onTestFailure(ITestResult result) { TestSuiteProgress.testFailed(testName(result), result.getThrowable()); } @Override public void onTestSkipped(ITestResult result) { TestSuiteProgress.testIgnored(testName(result)); } @Override public void onTestFailedButWithinSuccessPercentage(ITestResult result) { TestSuiteProgress.testFailed(testName(result), result.getThrowable()); } @Override public void onStart(ITestContext context) { } @Override public void onFinish(ITestContext context) { } private String testName(ITestResult res) { StringBuilder result = new StringBuilder(); result.append(res.getTestClass().getRealClass().getName()).append(".").append(res.getMethod().getMethodName()); if (res.getMethod().getConstructorOrMethod().getMethod().isAnnotationPresent(Test.class)) { String dataProviderName = res.getMethod().getConstructorOrMethod().getMethod().getAnnotation(Test.class) .dataProvider(); // Add parameters for methods that use a data provider only if (res.getParameters().length != 0 && (dataProviderName != null && !dataProviderName.isEmpty())) { result.append("(").append(Arrays.deepToString(res.getParameters())).append(")"); } } return result.toString(); } @Override public void onStart(ISuite isuite) { Set<Long> threads = new HashSet<>(); for (Map.Entry<Thread, StackTraceElement[]> s : Thread.getAllStackTraces().entrySet()) { Thread thread = s.getKey(); if (!thread.getName().startsWith("TestNG")) { threads.add(thread.getId()); } } startupThreads = threads; suiteRunning = true; } @Override public void onFinish(ISuite isuite) { // TestNG invokes this method twice, ignore it the second time boolean firstTime = suiteRunning; suiteRunning = false; if (!firstTime) return; int count = 0; for (Map.Entry<Thread, StackTraceElement[]> s : Thread.getAllStackTraces().entrySet()) { Thread thread = s.getKey(); if (ignoreThread(thread)) continue; if (count == 0) { log.warn("Possible leaked threads at the end of the test suite:"); } count++; // "management I/O-2" #55 prio=5 os_prio=0 tid=0x00007fe6a8134000 nid=0x7f9d runnable // [0x00007fe64e4db000] // java.lang.Thread.State:RUNNABLE log.warnf("\"%s\" #%d %sprio=%d tid=0x%x nid=NA %s", thread.getName(), count, thread.isDaemon() ? "daemon " : "", thread.getPriority(), thread.getId(), thread.getState().toString().toLowerCase()); log.warnf(" java.lang.Thread.State: %s", thread.getState()); for (StackTraceElement ste : s.getValue()) { log.warnf("\t%s", ste); } } } private boolean ignoreThread(Thread thread) { String threadName = thread.getName(); return threadName.startsWith("testng-") || threadName.startsWith("ForkJoinPool.commonPool-worker-") || startupThreads.contains(thread.getId()); } @Override public void beforeConfiguration(ITestResult testResult) { log.debugf("Before setup %s", testResult.getMethod().getMethodName()); } @Override public void onConfigurationSuccess(ITestResult testResult) { log.debugf("After setup %s", testResult.getMethod().getMethodName()); } @Override public void onConfigurationFailure(ITestResult testResult) { if (testResult.getThrowable() != null) { TestSuiteProgress.setupFailed(testName(testResult), testResult.getThrowable()); } } @Override public void onConfigurationSkip(ITestResult testResult) { if (testResult.getThrowable() != null) { TestSuiteProgress.testIgnored(testName(testResult)); } } }