diff options
3 files changed, 286 insertions, 10 deletions
diff --git a/luni/src/test/java/com/google/coretests/CoreTestRunner.java b/luni/src/test/java/com/google/coretests/CoreTestRunner.java index d80fa4e..82e9f54 100644 --- a/luni/src/test/java/com/google/coretests/CoreTestRunner.java +++ b/luni/src/test/java/com/google/coretests/CoreTestRunner.java @@ -69,6 +69,11 @@ public class CoreTestRunner extends TestRunner { private int fTimeout; private int fStep = 1; + + /** + * The path to write XML reports to, or {@code null} for no reports. + */ + private String xmlReportsDirectory; /** * Creates a new instance of our CoreTestRunner. @@ -127,11 +132,24 @@ public class CoreTestRunner extends TestRunner { * Make sure the original suite is unreachable after we have * created the new one, so GC can dispose terminated tests. */ - suite = new CoreTestSuite(suite, fFlags, fStep, null); - - return super.doRun(suite, wait); + CoreTestSuite coreTestSuite = new CoreTestSuite(suite, fFlags, fStep, null); + + XmlReportPrinter xmlReportPrinter = xmlReportsDirectory != null + ? new XmlReportPrinter(coreTestSuite) + : null; + + TestResult result = super.doRun(suite, wait); + + if (xmlReportPrinter != null) { + System.out.print("Printing XML Reports... "); + xmlReportPrinter.setResults(result); + int numFiles = xmlReportPrinter.generateReports(xmlReportsDirectory); + System.out.println(numFiles + " files written."); + } + + return result; } - + /** * Prints a help screen on the console. */ @@ -162,6 +180,7 @@ public class CoreTestRunner extends TestRunner { System.out.println(" --isolate-all"); System.out.println(" --isolate-none"); System.out.println(" --verbose"); + System.out.println(" --xml-reports-directory <path>"); System.out.println(" --help"); System.out.println(); System.out.println("Default parameters are:"); @@ -270,6 +289,8 @@ public class CoreTestRunner extends TestRunner { // victimName = args[++i]; } else if (args[i].equals("--dry-run")) { fFlags = fFlags | CoreTestSuite.DRY_RUN; + } else if (args[i].equals("--xml-reports-directory")) { + xmlReportsDirectory = args[++i]; } else if (args[i].equals("--help")) { showHelp(); System.exit(1); diff --git a/luni/src/test/java/com/google/coretests/CoreTestSuite.java b/luni/src/test/java/com/google/coretests/CoreTestSuite.java index 3c9e7fa..1828c57 100644 --- a/luni/src/test/java/com/google/coretests/CoreTestSuite.java +++ b/luni/src/test/java/com/google/coretests/CoreTestSuite.java @@ -98,6 +98,8 @@ public class CoreTestSuite implements Test { public static final int REVERSE = 512; public static final int DRY_RUN = 1024; + + private final String name; /** * The total number of tests in the original suite. @@ -154,7 +156,8 @@ public class CoreTestSuite implements Test { */ public CoreTestSuite(Test suite, int flags, int step, TestCase victim) { super(); - + + name = suite.toString(); fStep = step; addAndFlatten(suite, flags); fVictim = victim; @@ -269,27 +272,27 @@ public class CoreTestSuite implements Test { public void run(TestResult result) { // Run tests int i = 0; - + while (fTests.size() != 0 && !result.shouldStop()) { TestCase test = (TestCase)fTests.elementAt(i); Thread.currentThread().setContextClassLoader( test.getClass().getClassLoader()); - + test.run(result); /* if (fVictim != null) { TestResult dummy = fVictim.run(); - + if (dummy.failureCount() != 0) { result.addError(fTests.elementAt(i), new RuntimeException( - "Probable side effect", + "Probable side effect", ((TestFailure)dummy.failures().nextElement()). thrownException())); } else if (dummy.errorCount() != 0) { result.addError(fTests.elementAt(i), new RuntimeException( - "Probable side effect", + "Probable side effect", ((TestFailure)dummy.errors().nextElement()). thrownException())); } @@ -352,4 +355,7 @@ public class CoreTestSuite implements Test { return fTests.size(); } + @Override public String toString() { + return name; + } } diff --git a/luni/src/test/java/com/google/coretests/XmlReportPrinter.java b/luni/src/test/java/com/google/coretests/XmlReportPrinter.java new file mode 100644 index 0000000..315371f --- /dev/null +++ b/luni/src/test/java/com/google/coretests/XmlReportPrinter.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.google.coretests; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestFailure; +import junit.framework.TestResult; +import junit.runner.BaseTestRunner; +import org.kxml2.io.KXmlSerializer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + + +/** + * Writes JUnit results to a series of XML files in a format consistent with + * Ant's XMLJUnitResultFormatter. + * + * <p>Unlike Ant's formatter, this class does not report the execution time of + * tests. + */ +public class XmlReportPrinter { + + private static final String TESTSUITE = "testsuite"; + private static final String TESTCASE = "testcase"; + private static final String ERROR = "error"; + private static final String FAILURE = "failure"; + private static final String ATTR_NAME = "name"; + private static final String ATTR_TIME = "time"; + private static final String ATTR_ERRORS = "errors"; + private static final String ATTR_FAILURES = "failures"; + private static final String ATTR_TESTS = "tests"; + private static final String ATTR_TYPE = "type"; + private static final String ATTR_MESSAGE = "message"; + private static final String PROPERTIES = "properties"; + private static final String ATTR_CLASSNAME = "classname"; + private static final String TIMESTAMP = "timestamp"; + private static final String HOSTNAME = "hostname"; + + /** the XML namespace */ + private static final String ns = null; + + /** the test suites, which each contain tests */ + private final Map<String, Suite> suites = new LinkedHashMap<String, Suite>(); + + /** + * Create a report printer that prints the specified test suite. Since the + * CoreTestSuite nulls-out tests after they're run (to limit memory + * consumption), it is necessary to create the report printer prior to test + * execution. + */ + public XmlReportPrinter(CoreTestSuite allTests) { + // partition the tests by suite to be consistent with Ant's printer + for (Enumeration<Test> e = allTests.tests(); e.hasMoreElements(); ) { + TestId test = new TestId(e.nextElement()); + + // create the suite's entry in the map if necessary + Suite suite = suites.get(test.className); + if (suite == null) { + suite = new Suite(test.className); + suites.put(test.className, suite); + } + + suite.tests.add(test); + } + } + + public void setResults(TestResult result) { + populateFailures(true, result.errors()); + populateFailures(false, result.failures()); + } + + /** + * Populate the list of failures in each of the suites. + */ + private void populateFailures(boolean errors, Enumeration<TestFailure> failures) { + while (failures.hasMoreElements()) { + TestFailure failure = failures.nextElement(); + TestId test = new TestId(failure.failedTest()); + Suite suite = suites.get(test.className); + + if (suite == null) { + throw new IllegalStateException( "received a failure for a " + + "test that wasn't in the original test suite!"); + } + + if (errors) { + suite.errors.put(test, failure); + } else { + suite.failures.put(test, failure); + } + } + } + + public int generateReports(String directory) { + File parent = new File(directory); + parent.mkdirs(); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + TimeZone gmt = TimeZone.getTimeZone("GMT"); + dateFormat.setTimeZone(gmt); + dateFormat.setLenient(true); + String timestamp = dateFormat.format(new Date()); + + for (Suite suite : suites.values()) { + FileOutputStream stream = null; + try { + stream = new FileOutputStream(new File(parent, "TEST-" + suite.name + ".xml")); + + KXmlSerializer serializer = new KXmlSerializer(); + serializer.setOutput(stream, "UTF-8"); + serializer.startDocument("UTF-8", null); + serializer.setFeature( + "http://xmlpull.org/v1/doc/features.html#indent-output", true); + suite.print(serializer, timestamp); + serializer.endDocument(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException ignored) { + } + } + } + } + + return suites.size(); + } + + static class Suite { + private final String name; + private final List<TestId> tests = new ArrayList<TestId>(); + private final Map<TestId, TestFailure> failures = new HashMap<TestId, TestFailure>(); + private final Map<TestId, TestFailure> errors = new HashMap<TestId, TestFailure>(); + + Suite(String name) { + this.name = name; + } + + void print(KXmlSerializer serializer, String timestamp) throws IOException { + serializer.startTag(ns, TESTSUITE); + serializer.attribute(ns, ATTR_NAME, name); + serializer.attribute(ns, ATTR_TESTS, Integer.toString(tests.size())); + serializer.attribute(ns, ATTR_FAILURES, Integer.toString(failures.size())); + serializer.attribute(ns, ATTR_ERRORS, Integer.toString(errors.size())); + serializer.attribute(ns, ATTR_TIME, "0"); + + serializer.attribute(ns, TIMESTAMP, timestamp); + serializer.attribute(ns, HOSTNAME, "localhost"); + serializer.startTag(ns, PROPERTIES); + serializer.endTag(ns, PROPERTIES); + + for (TestId testId : tests) { + TestFailure error = errors.get(testId); + TestFailure failure = failures.get(testId); + + if (error != null) { + testId.printFailure(serializer, ERROR, error.thrownException()); + } else if (failure != null) { + testId.printFailure(serializer, FAILURE, failure.thrownException()); + } else { + testId.printSuccess(serializer); + } + } + + serializer.endTag(ns, TESTSUITE); + } + } + + private static class TestId { + private final String name; + private final String className; + + TestId(Test test) { + this.name = test instanceof TestCase + ? ((TestCase) test).getName() + : test.toString(); + this.className = test.getClass().getName(); + } + + void printSuccess(KXmlSerializer serializer) throws IOException { + serializer.startTag(ns, TESTCASE); + printAttributes(serializer); + serializer.endTag(ns, TESTCASE); + } + + void printFailure(KXmlSerializer serializer, String type, Throwable t) + throws IOException { + serializer.startTag(ns, TESTCASE); + printAttributes(serializer); + + serializer.startTag(ns, type); + String message = t.getMessage(); + if (message != null && message.length() > 0) { + serializer.attribute(ns, ATTR_MESSAGE, t.getMessage()); + } + serializer.attribute(ns, ATTR_TYPE, t.getClass().getName()); + serializer.text(BaseTestRunner.getFilteredTrace(t)); + serializer.endTag(ns, type); + + serializer.endTag(ns, TESTCASE); + } + + void printAttributes(KXmlSerializer serializer) throws IOException { + serializer.attribute(ns, ATTR_NAME, name); + serializer.attribute(ns, ATTR_CLASSNAME, className); + serializer.attribute(ns, ATTR_TIME, "0"); + } + + @Override public boolean equals(Object o) { + return o instanceof TestId + && ((TestId) o).name.equals(name) + && ((TestId) o).className.equals(className); + } + + @Override public int hashCode() { + return name.hashCode() ^ className.hashCode(); + } + } +}
\ No newline at end of file |