blob: edd9a122f0d01861bbe0541e852b5408509463d5 [file] [log] [blame]
/*
* 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) {
ignored.printStackTrace();
}
}
}
}
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();
}
}
}