All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.testng.reporters.JUnitXMLReporter Maven / Gradle / Ivy

package org.testng.reporters;


import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.ITestResult;
import org.testng.collections.Lists;
import org.testng.collections.Maps;
import org.testng.collections.Sets;
import org.testng.internal.IResultListener2;
import org.testng.internal.Utils;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Collections;
import java.util.Collection;
import java.util.Queue;
import java.util.TimeZone;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.regex.Pattern;

import java.text.SimpleDateFormat;

/**
 * A JUnit XML report generator (replacing the original JUnitXMLReporter that was
 * based on XML APIs).
 *
 * @author Alex Popescu
 */
public class JUnitXMLReporter implements IResultListener2 {
  private static final Pattern ENTITY= Pattern.compile("&[a-zA-Z]+;.*");
  private static final Pattern LESS= Pattern.compile("<");
  private static final Pattern GREATER= Pattern.compile(">");
  private static final Pattern SINGLE_QUOTE = Pattern.compile("'");
  private static final Pattern QUOTE = Pattern.compile("\"");
  private static final Map ATTR_ESCAPES= Maps.newHashMap();

  static {
    ATTR_ESCAPES.put("<", LESS);
    ATTR_ESCAPES.put(">", GREATER);
    ATTR_ESCAPES.put("'", SINGLE_QUOTE);
    ATTR_ESCAPES.put(""", QUOTE);
  }


  private int m_numFailed= 0;
  private Queue m_allTests = new ConcurrentLinkedDeque<>();
  private Queue m_configIssues = new ConcurrentLinkedDeque<>();
  private Map m_fileNameMap = Maps.newHashMap();
  private int m_fileNameIncrementer = 0;

  @Override
  public void onTestStart(ITestResult result) {
  }

  @Override
  public void beforeConfiguration(ITestResult tr) {
  }

  /**
   * Invoked each time a test succeeds.
   */
  @Override
  public void onTestSuccess(ITestResult tr) {
    m_allTests.add(tr);
  }

  @Override
  public void onTestFailedButWithinSuccessPercentage(ITestResult tr) {
    m_allTests.add(tr);
  }

  /**
   * Invoked each time a test fails.
   */
  @Override
  public void onTestFailure(ITestResult tr) {
    m_allTests.add(tr);
    m_numFailed++;
  }

  /**
   * Invoked each time a test is skipped.
   */
  @Override
  public void onTestSkipped(ITestResult tr) {
    m_allTests.add(tr);
  }

  /**
   * Invoked after the test class is instantiated and before
   * any configuration method is called.
   *
   */
  @Override
  public void onStart(ITestContext context) {

  }

  /**
   * Invoked after all the tests have run and all their
   * Configuration methods have been called.
   *
   */
  @Override
  public void onFinish(ITestContext context) {
	generateReport(context);
    resetAll();
  }

  /**
   * @see org.testng.IConfigurationListener#onConfigurationFailure(org.testng.ITestResult)
   */
  @Override
  public void onConfigurationFailure(ITestResult itr) {
    m_configIssues.add(itr);
  }

  /**
   * @see org.testng.IConfigurationListener#onConfigurationSkip(org.testng.ITestResult)
   */
  @Override
  public void onConfigurationSkip(ITestResult itr) {
    m_configIssues.add(itr);
  }

  /**
   * @see org.testng.IConfigurationListener#onConfigurationSuccess(org.testng.ITestResult)
   */
  @Override
  public void onConfigurationSuccess(ITestResult itr) {
  }

  /**
   * generate the XML report given what we know from all the test results
   */
  protected void generateReport(ITestContext context) {

      XMLStringBuffer document= new XMLStringBuffer();
      document.addComment("Generated by " + getClass().getName());

      Properties attrs= new Properties();
      attrs.setProperty(XMLConstants.ATTR_ERRORS, "0");
      attrs.setProperty(XMLConstants.ATTR_FAILURES, Integer.toString(m_numFailed));
      attrs.setProperty(XMLConstants.ATTR_IGNORED, Integer.toString(context.getExcludedMethods().size()));
      try {
        attrs.setProperty(XMLConstants.ATTR_HOSTNAME, InetAddress.getLocalHost().getHostName());
      } catch (UnknownHostException e) {
        // ignore
      }
      Set packages = getPackages(context);
      if (packages.size() > 0) {
        attrs.setProperty(XMLConstants.ATTR_NAME, context.getCurrentXmlTest().getName());
//        attrs.setProperty(XMLConstants.ATTR_PACKAGE, packages.iterator().next());
      }

      attrs.setProperty(XMLConstants.ATTR_TESTS, Integer.toString(m_allTests.size()));
      attrs.setProperty(XMLConstants.ATTR_TIME,
          Double.toString((context.getEndDate().getTime() - context.getStartDate().getTime()) / 1000.0));

      attrs.setProperty(XMLConstants.ATTR_TIMESTAMP, timeAsGmt());

      document.push(XMLConstants.TESTSUITE, attrs);

      createElementFromTestResults(document, m_configIssues);
      createElementFromTestResults(document, m_allTests);
      createElementFromIgnoredTests(document, context);

      document.pop();
      Utils.writeUtf8File(context.getOutputDirectory(),generateFileName(context) + ".xml", document.toXML());
  }

  static String timeAsGmt() {
    SimpleDateFormat sdf = new SimpleDateFormat();
    sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
    sdf.applyPattern("dd MMM yyyy HH:mm:ss z");
    return sdf.format(Calendar.getInstance().getTime());
  }

  private synchronized void createElementFromTestResults(XMLStringBuffer document, Collection results) {
    for(ITestResult tr : results) {
      createElement(document, tr);
    }
  }

  private synchronized void createElementFromIgnoredTests(XMLStringBuffer doc, ITestContext context) {
    Collection methods = context.getExcludedMethods();
    for (ITestNGMethod method : methods) {
      Properties properties = getPropertiesFor(method, 0);
      doc.push(XMLConstants.TESTCASE,properties);
      doc.addEmptyElement(XMLConstants.ATTR_IGNORED);
      doc.pop();
    }
  }

  private Properties getPropertiesFor(ITestNGMethod method, long elapsedTimeMillis) {
    Properties attrs= new Properties();
    String name= Utils.detailedMethodName(method, false);
    attrs.setProperty(XMLConstants.ATTR_NAME, name);
    attrs.setProperty(XMLConstants.ATTR_CLASSNAME, method.getRealClass().getName());
    attrs.setProperty(XMLConstants.ATTR_TIME, Double.toString(((double) elapsedTimeMillis) / 1000));
    return attrs;
  }


  private Set getPackages(ITestContext context) {
    Set result = Sets.newHashSet();
    for (ITestNGMethod m : context.getAllTestMethods()) {
      Package pkg = m.getConstructorOrMethod().getDeclaringClass().getPackage();
      if (pkg != null) {
        result.add(pkg.getName());
      }
    }
    return result;
  }

  private void createElement(XMLStringBuffer doc, ITestResult tr) {

    long elapsedTimeMillis= tr.getEndMillis() - tr.getStartMillis();
    Properties attrs= getPropertiesFor(tr.getMethod(), elapsedTimeMillis);
    if (tr.getMethod().isTest()) {
      attrs.setProperty(XMLConstants.ATTR_NAME, tr.getName());
    }

    if((ITestResult.FAILURE == tr.getStatus()) || (ITestResult.SKIP == tr.getStatus())) {
      doc.push(XMLConstants.TESTCASE, attrs);

      if(ITestResult.FAILURE == tr.getStatus()) {
        createFailureElement(doc, tr);
      }
      else if(ITestResult.SKIP == tr.getStatus()) {
        createSkipElement(doc);
      }

      doc.pop();
    }
    else {
      doc.addEmptyElement(XMLConstants.TESTCASE, attrs);
    }
  }

  private void createFailureElement(XMLStringBuffer doc, ITestResult tr) {
    Properties attrs= new Properties();
    Throwable t= tr.getThrowable();
    if(t != null) {
      attrs.setProperty(XMLConstants.ATTR_TYPE, t.getClass().getName());
      String message= t.getMessage();
      if((message != null) && (message.length() > 0)) {
        attrs.setProperty(XMLConstants.ATTR_MESSAGE, encodeAttr(message)); // ENCODE
      }
      doc.push(XMLConstants.FAILURE, attrs);
      doc.addCDATA(Utils.shortStackTrace(t, false));
      doc.pop();
    }
    else {
      doc.addEmptyElement(XMLConstants.FAILURE); // THIS IS AN ERROR
    }
  }

  private void createSkipElement(XMLStringBuffer doc) {
    doc.addEmptyElement(XMLConstants.SKIPPED);
  }

  private String encodeAttr(String attr) {
    String result= replaceAmpersand(attr, ENTITY);
    for(Map.Entry e: ATTR_ESCAPES.entrySet()) {
      result= e.getValue().matcher(result).replaceAll(e.getKey());
    }

    return result;
  }

  private String replaceAmpersand(String str, Pattern pattern) {
    int start = 0;
    int idx = str.indexOf('&', start);
    if(idx == -1) {
      return str;
    }
    StringBuilder result= new StringBuilder();
    while(idx != -1) {
      result.append(str.substring(start, idx));
      if(pattern.matcher(str.substring(idx)).matches()) {
        // do nothing it is an entity;
        result.append("&");
      }
      else {
        result.append("&");
      }
      start= idx + 1;
      idx= str.indexOf('&', start);
    }
    result.append(str.substring(start));

    return result.toString();
  }


  /**
	 * Reset all member variables for next test.
	 * */
	private void resetAll() {
		m_allTests = new ConcurrentLinkedDeque<>();
		m_configIssues = new ConcurrentLinkedDeque<>();
		m_numFailed = 0;
    }

	/**
	 * This method guarantees unique file names for reports.
* Also, this will guarantee that the old reports are overwritten when tests are run again. * * @param context * test context * @return unique name for the file associated with this test context. * */ private String generateFileName(ITestContext context) { String fileName; String keyToSearch = context.getSuite().getName() + context.getName(); if (m_fileNameMap.get(keyToSearch) == null) { fileName = context.getName(); } else { fileName = context.getName() + m_fileNameIncrementer++; } m_fileNameMap.put(keyToSearch, fileName); return fileName; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy