package org.testng.reporters;
import org.testng.ISuite;
import org.testng.ISuiteListener;
import org.testng.ITestClass;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.TestRunner;
import org.testng.internal.Utils;
import org.testng.xml.XmlClass;
import org.testng.xml.XmlSuite;
import org.testng.xml.XmlTest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A very custom ISuiteListener that will generate the testng-failed.xml definition.
* <P/>
* In case of a failure we need the following info:
* - suite-verbose, parallel, thread-count, annotations
* - all parameters declared in the suite
* - all parameters declared in the enclosing test definition of the failed one
*
* @author <a href='mailto:the_mindstorm@evolva.ro'>Alexandru Popescu</a>
*/
public class FailedTestSuiteGenerator implements ITestListener, ISuiteListener {
private XmlSuite m_xmlSuite;
private boolean m_hasFailures;
private String m_currentTestName;
private Map<String, XmlTest> m_testNames= new HashMap<String, XmlTest>();
private Map<String, XmlTest> m_failedTestNames= new HashMap<String, XmlTest>();
private Map<String, XmlTestMethods> m_testNameMethods= new HashMap<String, XmlTestMethods>();
private Map<String, String> m_outputs= new HashMap<String, String>();
private Map<Method, ITestNGMethod> m_failedMethodConf= new HashMap<Method, ITestNGMethod>();
/** Map<class_fqn, Set<Method>>. */
private Map<String, Set<Method>> m_failedMethodConfPClass= new HashMap<String, Set<Method>>();
private Map<Method, ITestNGMethod> m_failedClassConfs= new HashMap<Method, ITestNGMethod>();
/** Map<class_fqn, Set<Method>>. */
private Map<String, Set<Method>> m_failedClassConfPClass= new HashMap<String, Set<Method>>();
private Map<Method, ITestNGMethod> m_failedTestConfs= new HashMap<Method, ITestNGMethod>();
private Map<Method, ITestNGMethod> m_failedSuiteConfs= new HashMap<Method, ITestNGMethod>();
public FailedTestSuiteGenerator(XmlSuite xmlSuite) {
m_xmlSuite= xmlSuite;
for (XmlTest xt: xmlSuite.getTests()) {
m_testNames.put(xt.getName(), xt);
}
}
public void registerRunner(TestRunner tr) {
XmlTest xmlTest= tr.getTest();
String testName= xmlTest.getName();
if (!m_testNames.containsKey(testName)) {
System.err.println("[ERROR]: the test " + testName + " wasn't registered!");
m_testNames.put(testName, xmlTest);
}
m_outputs.put(testName, tr.getOutputDirectory());
m_testNameMethods.put(testName, new XmlTestMethods(xmlTest, tr.getIClass()));
}
public void onStart(ISuite suite) {
;
}
public void onFinish(ISuite suite) {
if (!m_hasFailures) {
return; // nothing to generate
}
for(String testname: m_failedTestNames.keySet()) {
m_testNameMethods.get(testname).initialize();
}
Map<String, Set<String>> testperOut= regroupByOutput(m_outputs);
for(String outdir: testperOut.keySet()) {
Set<String> testnames= testperOut.get(outdir);
XmlSuite newsuite= generateSuite(testnames);
Utils.writeFile(outdir, "testng-f.xml", newsuite.toXml());
}
}
public void onTestFailure(ITestResult result) {
registerFailedTest(m_currentTestName, result);
}
public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
registerFailedTest(m_currentTestName, result);
}
public void onTestSkipped(ITestResult result) {
registerFailedTest(m_currentTestName, result);
}
public void onStart(ITestContext context) {
m_currentTestName= context.getName();
}
public void onFinish(ITestContext context) {
m_currentTestName= null;
}
private void registerFailedTest(final String testName, final ITestResult result) {
m_hasFailures= true;
ITestNGMethod mth= result.getMethod();
if (null != testName && mth.isTest()) {
XmlTestMethods xmlTestMethods= m_testNameMethods.get(testName);
xmlTestMethods.m_failedTestMethods.add(mth);
m_failedTestNames.put(testName, m_testNames.get(testName));
}
else if (mth.isBeforeMethodConfiguration() || mth.isAfterMethodConfiguration()) {
m_failedMethodConf.put(mth.getMethod(), mth);
registerClassMethod(m_failedMethodConfPClass ,mth);
}
else if (mth.isBeforeClassConfiguration() || mth.isAfterClassConfiguration()) {
m_failedClassConfs.put(mth.getMethod(), mth);
registerClassMethod(m_failedClassConfPClass, mth);
}
else if (mth.isBeforeTestConfiguration() || mth.isAfterTestConfiguration()) {
m_failedTestConfs.put(mth.getMethod(), mth);
}
else if (mth.isBeforeSuiteConfiguration() || mth.isAfterSuiteConfiguration()) {
m_failedSuiteConfs.put(mth.getMethod(), mth);
}
}
private void registerClassMethod(Map<String, Set<Method>> classMethods, ITestNGMethod itm) {
String classFqn= itm.getRealClass().getName();
Set<Method> methods= classMethods.get(classFqn);
if (null == methods) {
methods= new HashSet<Method>();
classMethods.put(classFqn, methods);
}
methods.add(itm.getMethod());
}
private XmlSuite generateSuite(Set<String> testnames) {
// System.out.println("GenerateSuiteForOutput:[" + output + "]");
XmlSuite failedSuite= new XmlSuite();
failedSuite.setAnnotations(m_xmlSuite.getAnnotations());
failedSuite.setName("Failed Tests suite");
failedSuite.setParallel(m_xmlSuite.isParallel());
failedSuite.setThreadCount(m_xmlSuite.getThreadCount());
failedSuite.setParameters(m_xmlSuite.getParameters());
for(String name: testnames) {
// TODO: check also the beforeSuite/afterSuite failed/skipped methods
if (!m_failedTestNames.containsKey(name)) {
continue;
}
XmlTestMethods xtm= m_testNameMethods.get(name);
if (!m_failedSuiteConfs.isEmpty()) {
// regenerate the full suite as long as a before/afterSuite was failed
}
if (xtm.hasTestConfigurationFailures()) {
// regenerate full XmlTest as long as a before/afterTest was failed
clone(failedSuite, m_failedTestNames.get(name));
}
else {
generatePartialTest(failedSuite, name);
}
}
return failedSuite;
}
private void generatePartialTest(XmlSuite suite, String testname) {
XmlTest srcTest= m_testNames.get(testname);
XmlTest nwXmlTest= cloneTestDef(suite, srcTest);
XmlTestMethods testMethods= m_testNameMethods.get(testname);
testMethods.initialize();
for(XmlClass xmlClass: srcTest.getXmlClasses()) {
String fqn= xmlClass.getSupportClass().getName();
if (m_failedMethodConfPClass.containsKey(fqn) || m_failedClassConfPClass.containsKey(fqn)) {
nwXmlTest.getXmlClasses().add(xmlClass);
}
else {
// initially duplicate the class
XmlClass nwXmlClass= clone(xmlClass);
ITestClass itc= testMethods.m_testClasses.get(fqn);
List<ITestNGMethod> nonfailed= getNonFailedMethods(testMethods, fqn);
for(ITestNGMethod itm: nonfailed) {
nwXmlClass.getExcludedMethods().add(itm.getMethod().getName());
}
nwXmlTest.getXmlClasses().add(nwXmlClass);
}
}
}
private List<ITestNGMethod> getNonFailedMethods(XmlTestMethods tmethods, String classFqn) {
ITestClass itc= tmethods.m_testClasses.get(classFqn);
List<ITestNGMethod> result= new ArrayList<ITestNGMethod>();
List<ITestNGMethod> failedMethods= tmethods.m_failedMethodsPClass.get(classFqn);
if (null != failedMethods) {
Map<Method, ITestNGMethod> failedMethodsMap= new HashMap<Method, ITestNGMethod>();
for(ITestNGMethod itm: failedMethods) {
failedMethodsMap.put(itm.getMethod(), itm);
}
for(ITestNGMethod itm: itc.getTestMethods()) {
if (!failedMethodsMap.containsKey(itm.getMethod())) {
result.add(itm);
}
}
}
else {
for(ITestNGMethod itm: itc.getTestMethods()) {
result.add(itm);
}
}
return result;
}
/**
* Clone the <TT>source</TT> <CODE>XmlTest</CODE> by including:
* - test attributes
* - groups definitions
* - parameters
*
* The <classes> subelement is ignored for the moment.
*
* @param suite
* @param source
* @return
*/
private XmlTest cloneTestDef(XmlSuite suite, XmlTest source) {
XmlTest result= new XmlTest(suite);
result.setName(source.getName());
result.setAnnotations(source.getAnnotations());
result.setIncludedGroups(source.getIncludedGroups());
result.setExcludedGroups(source.getExcludedGroups());
result.setJUnit(source.isJUnit());
result.setParallel(source.isParallel());
result.setVerbose(source.getVerbose());
result.setParameters(source.getParameters());
Map<String, List<String>> metagroups= source.getMetaGroups();
for (String groupName: metagroups.keySet()) {
result.addMetaGroup(groupName, metagroups.get(groupName));
}
return result;
}
private XmlTest clone(XmlSuite suite, XmlTest source) {
XmlTest result= cloneTestDef(suite, source);
result.setClassNames(source.getXmlClasses());
return result;
}
private XmlClass clone(XmlClass source) {
XmlClass newclass= new XmlClass(source.getName());
newclass.setExcludedMethods(source.getExcludedMethods());
newclass.setIncludedMethods(source.getIncludedMethods());
return newclass;
}
private void addMethodDefinitions(XmlTest test, Map<String, List<ITestNGMethod>> classMethods) {
List<XmlClass> classes= new ArrayList<XmlClass>();
for (String className: classMethods.keySet()) {
XmlClass xmlClass= new XmlClass(className);
List<String> methodNames= new ArrayList<String>();
for(ITestNGMethod itm: classMethods.get(className)) {
methodNames.add(itm.getMethod().getName());
}
xmlClass.setIncludedMethods(methodNames);
classes.add(xmlClass);
}
test.setClassNames(classes);
}
public void onTestSuccess(ITestResult result) {
;
}
private Map<String, Set<String>> regroupByOutput(Map<String, String> outputs) {
Map<String, Set<String>> result= new HashMap<String, Set<String>>();
for(String testname: outputs.keySet()) {
String outdir= outputs.get(testname);
Set<String> names= result.get(outdir);
if (null == names) {
names= new HashSet<String>();
result.put(outdir, names);
}
names.add(testname);
}
return result;
}
private static class XmlTestMethods {
private XmlTest m_xmlTest;
private Map<String, ITestClass> m_testClasses= new HashMap<String, ITestClass>();
private List<ITestNGMethod> m_failedTestMethods= new ArrayList<ITestNGMethod>();
private Map<String, XmlClass> m_classes= new HashMap<String, XmlClass>();
private Map<String, List<ITestNGMethod>> m_failedMethodsPClass= new HashMap<String, List<ITestNGMethod>>();
public XmlTestMethods(XmlTest xmlTest, Collection<ITestClass> testClasses) {
m_xmlTest= xmlTest;
for(ITestClass itc: testClasses) {
m_testClasses.put(itc.getRealClass().getName(), itc);
}
}
public void initialize() {
for(XmlClass xmlClazz: m_xmlTest.getXmlClasses()) {
m_classes.put(xmlClazz.getName(), xmlClazz);
}
for(ITestNGMethod itm: m_failedTestMethods) {
String fqn= itm.getRealClass().getName();
List<ITestNGMethod> methods= m_failedMethodsPClass.get(fqn);
if (null == methods) {
methods= new ArrayList<ITestNGMethod>();
m_failedMethodsPClass.put(fqn, methods);
}
methods.add(itm);
}
}
/**
* TODO
* @return
*/
public boolean hasTestConfigurationFailures() {
return false;
}
}
}