From c874f96228f13f7ca2fb6eff18c1016580887de7 Mon Sep 17 00:00:00 2001 From: Marek Baluch Date: Thu, 1 Sep 2016 10:17:08 +0200 Subject: [PATCH] A class which generates a single junit xml file. This file will be consumed by Polarion --- testsuite/integration-arquillian/pom.xml | 1 + .../integration-arquillian/test-utils/pom.xml | 34 +++ .../util/junit/AggregateResultsReporter.java | 269 ++++++++++++++++++ .../integration-arquillian/tests/pom.xml | 11 +- 4 files changed, 313 insertions(+), 2 deletions(-) create mode 100644 testsuite/integration-arquillian/test-utils/pom.xml create mode 100644 testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index 977fba877c..4a4cdce18b 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -119,6 +119,7 @@ test-apps + test-utils servers tests diff --git a/testsuite/integration-arquillian/test-utils/pom.xml b/testsuite/integration-arquillian/test-utils/pom.xml new file mode 100644 index 0000000000..c03c56b334 --- /dev/null +++ b/testsuite/integration-arquillian/test-utils/pom.xml @@ -0,0 +1,34 @@ + + + + integration-arquillian + org.keycloak.testsuite + 2.2.0-SNAPSHOT + + 4.0.0 + + integration-arquillian-test-utils + jar + + Test utils + + + + junit + junit + compile + + + org.jboss.logging + jboss-logging + + + commons-configuration + commons-configuration + 1.10 + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java b/testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java new file mode 100644 index 0000000000..a0809c126c --- /dev/null +++ b/testsuite/integration-arquillian/test-utils/src/main/java/org/keycloak/testsuite/util/junit/AggregateResultsReporter.java @@ -0,0 +1,269 @@ +package org.keycloak.testsuite.util.junit; + +import org.apache.commons.configuration.PropertiesConfiguration; + +import org.jboss.logging.Logger; + +import org.junit.Ignore; +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Aggregates jUnit test results into a single report - XML file. + */ +public class AggregateResultsReporter extends RunListener { + + private static final Logger LOGGER = Logger.getLogger(AggregateResultsReporter.class); + + private final Document xml; + private final File reportFile; + private final boolean working; + + private final AtomicInteger tests = new AtomicInteger(0); + private final AtomicInteger errors = new AtomicInteger(0); + private final AtomicInteger failures = new AtomicInteger(0); + private final AtomicInteger ignored = new AtomicInteger(0); + private final AtomicLong suiteStartTime = new AtomicLong(0L); + + private final AtomicReference testsuite = new AtomicReference(); + + private final Map testTimes = new HashMap(); + + public AggregateResultsReporter() { + boolean working = true; + Document xml = null; + try { + xml = createEmptyDocument(); + } catch (ParserConfigurationException ex) { + LOGGER.error("Failed to create XML DOM - reporting will not be done", ex); + working = false; + } + + File reportFile = null; + try { + reportFile = createReportFile(); + } catch (Exception ex) { + LOGGER.error("Failed to create log file - reporting will not be done", ex); + working = false; + } + + this.working = working; + this.xml = xml; + this.reportFile = reportFile; + } + + private Document createEmptyDocument() throws ParserConfigurationException { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + return builder.newDocument(); + } + + private File createReportFile() throws Exception { + PropertiesConfiguration config = new PropertiesConfiguration(System.getProperty("testsuite.constants")); + config.setThrowExceptionOnMissing(true); + + final File logDir = new File(config.getString("log-dir")); + logDir.mkdirs(); + + final File reportFile = new File(logDir, "junit-report.xml").getAbsoluteFile(); + reportFile.createNewFile(); + + return reportFile; + } + + @Override + public void testRunStarted(Description description) throws Exception { + if (working) { + suiteStartTime.set(System.currentTimeMillis()); + + Element testsuite = xml.createElement("testsuite"); + + if (description.getChildren().size() == 1) { + testsuite.setAttribute("name", safeString(description.getChildren().get(0).getDisplayName())); + } + + xml.appendChild(testsuite); + this.testsuite.set(testsuite); + writeXml(); + } + } + + @Override + public void testStarted(Description description) throws Exception { + if (working) { + testTimes.put(description.getDisplayName(), System.currentTimeMillis()); + } + } + + @Override + public void testFinished(Description description) throws Exception { + if (working) { + if (testTimes.containsKey(description.getDisplayName())) { + testsuite.get().appendChild(createTestCase(description)); + writeXml(); + } + } + } + + @Override + public void testAssumptionFailure(Failure failure) { + if (working) { + ignored.incrementAndGet(); + + Element testcase = createTestCase(failure.getDescription()); + Element skipped = xml.createElement("skipped"); + skipped.setAttribute("message", safeString(failure.getMessage())); + + testcase.appendChild(skipped); + + testsuite.get().appendChild(testcase); + writeXml(); + } + } + + @Override + public void testFailure(Failure failure) throws Exception { + if (working) { + if (failure.getDescription().getMethodName() == null) { + // before class failed + for (Description child : failure.getDescription().getChildren()) { + // mark all methods failed + testFailure(new Failure(child, failure.getException())); + } + } else { + // normal failure + Element testcase = createTestCase(failure.getDescription()); + + Element element; + if (failure.getException() instanceof AssertionError) { + failures.incrementAndGet(); + element = xml.createElement("failure"); + } else { + errors.incrementAndGet(); + element = xml.createElement("error"); + } + + testcase.appendChild(element); + + element.setAttribute("type", safeString(failure.getException().getClass().getName())); + element.setAttribute("message", safeString(failure.getMessage())); + element.appendChild(xml.createCDATASection(safeString(failure.getTrace()))); + + testsuite.get().appendChild(testcase); + writeXml(); + } + } + } + + @Override + public void testIgnored(Description description) throws Exception { + if (working) { + ignored.incrementAndGet(); + + Element testcase = createTestCase(description); + + Element skipped = xml.createElement("skipped"); + skipped.setAttribute("message", safeString(description.getAnnotation(Ignore.class).value())); + + testcase.appendChild(skipped); + + testsuite.get().appendChild(testcase); + writeXml(); + } + } + + @Override + public void testRunFinished(Result result) throws Exception { + if (working) { + writeXml(); + } + } + + private void writeXml() { + Element testsuite = this.testsuite.get(); + + testsuite.setAttribute("tests", Integer.toString(tests.get())); + testsuite.setAttribute("errors", Integer.toString(errors.get())); + testsuite.setAttribute("skipped", Integer.toString(ignored.get())); + testsuite.setAttribute("failures", Integer.toString(failures.get())); + testsuite.setAttribute("time", computeTestTime(suiteStartTime.get())); + + try { + Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(reportFile, false), Charset.forName("UTF-8"))); + try { + Transformer t = TransformerFactory.newInstance().newTransformer(); + t.setOutputProperty(OutputKeys.INDENT, "yes"); + t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + t.transform(new DOMSource(xml), new StreamResult(writer)); + } catch (TransformerConfigurationException ex) { + LOGGER.error("Misconfigured transformer", ex); + } catch (TransformerException ex) { + LOGGER.error("Unable to save XML file", ex); + } finally { + writer.close(); + } + } catch (IOException ex) { + LOGGER.warn("Unable to open report file", ex); + } + } + + private String computeTestTime(Long startTime) { + if (startTime == null) { + return "0"; + } else { + long amount = System.currentTimeMillis() - startTime; + return String.format("%.3f", amount / 1000F); + } + } + + private Element createTestCase(Description description) { + tests.incrementAndGet(); + + Element testcase = xml.createElement("testcase"); + + testcase.setAttribute("name", safeString(description.getMethodName())); + testcase.setAttribute("classname", safeString(description.getClassName())); + testcase.setAttribute("time", computeTestTime(testTimes.remove(description.getDisplayName()))); + + return testcase; + } + + private String safeString(String input) { + if (input == null) { + return "null"; + } + + return input + // first remove color coding (all of it) + .replaceAll("\u001b\\[\\d+m", "") + // then remove control characters that are not whitespaces + .replaceAll("[\\p{Cntrl}&&[^\\p{Space}]]", ""); + } +} diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index 6c250598a4..0a1fddfe6d 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -37,7 +37,7 @@ base other - + undertow true @@ -178,7 +178,7 @@ listener - org.keycloak.testsuite.util.TestEventsLogger + org.keycloak.testsuite.util.TestEventsLogger,org.keycloak.testsuite.util.junit.AggregateResultsReporter @@ -645,6 +645,13 @@ + + + org.keycloak.testsuite + integration-arquillian-test-utils + ${project.version} + + junit