aboutsummaryrefslogtreecommitdiffstats
path: root/tests/junit-runner
diff options
context:
space:
mode:
Diffstat (limited to 'tests/junit-runner')
-rw-r--r--tests/junit-runner/CommandLine.java3
-rw-r--r--tests/junit-runner/JunitLikeXmlOutputListener.java225
2 files changed, 228 insertions, 0 deletions
diff --git a/tests/junit-runner/CommandLine.java b/tests/junit-runner/CommandLine.java
index ad7dc7e..1807c65 100644
--- a/tests/junit-runner/CommandLine.java
+++ b/tests/junit-runner/CommandLine.java
@@ -7,6 +7,7 @@
* http://www.eclipse.org/legal/cpl-v10.html
*/
+import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -41,6 +42,8 @@ public class CommandLine extends JUnitCore {
system.out().println("ERROR: Could not find class: " + each);
}
}
+ RunListener jXmlOutput = new JunitLikeXmlOutputListener(system, new File("tests-output.xml"));
+ addListener(jXmlOutput);
RunListener listener = new LessVerboseTextListener(system);
addListener(listener);
Result result = run(classes.toArray(new Class[0]));
diff --git a/tests/junit-runner/JunitLikeXmlOutputListener.java b/tests/junit-runner/JunitLikeXmlOutputListener.java
new file mode 100644
index 0000000..34efc64
--- /dev/null
+++ b/tests/junit-runner/JunitLikeXmlOutputListener.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2011 Red Hat, Inc.
+ *
+ * This file is made available under the terms of the Common Public License
+ * v1.0 which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+
+import java.io.BufferedWriter;
+import java.io.File;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+
+import org.junit.internal.JUnitSystem;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+/**
+ * This class listens for events in junit testsuite and wrote output to xml.
+ * Xml tryes to follow ant-tests schema, and is enriched for by-class statistics
+ * stdout and err elements are added, but must be filled from elsewhere (eg tee
+ * in make) as junit suite and listener run from our executer have no access to
+ * them.
+ *
+ */
+public class JunitLikeXmlOutputListener extends RunListener {
+
+ private BufferedWriter writer;
+ private Failure testFailed = null;
+ private static final String ROOT = "testsuite";
+ private static final String DATE_ELEMENT = "date";
+ private static final String TEST_ELEMENT = "testcase";
+ private static final String TEST_NAME_ATTRIBUTE = "name";
+ private static final String TEST_TIME_ATTRIBUTE = "time";
+ private static final String TEST_ERROR_ELEMENT = "error";
+ private static final String TEST_CLASS_ATTRIBUTE = "classname";
+ private static final String ERROR_MESSAGE_ATTRIBUTE = "message";
+ private static final String ERROR_TYPE_ATTRIBUTE = "type";
+ private static final String SOUT_ELEMENT = "system-out";
+ private static final String SERR_ELEMENT = "system-err";
+ private static final String CDATA_START = "<![CDATA[";
+ private static final String CDATA_END = "]]>";
+ private static final String TEST_CLASS_ELEMENT = "class";
+ private static final String STATS_ELEMENT = "stats";
+ private static final String CLASSES_ELEMENT = "classes";
+ private static final String SUMMARY_ELEMENT = "summary";
+ private static final String SUMMARY_TOTAL_ELEMENT = "total";
+ private static final String SUMMARY_PASSED_ELEMENT = "passed";
+ private static final String SUMMARY_FAILED_ELEMENT = "failed";
+ private static final String SUMMARY_IGNORED_ELEMENT = "ignored";
+ private long testStart;
+
+ private class ClassCounter {
+
+ int total;
+ int failed;
+ int passed;
+ long time = 0;
+ }
+ Map<String, ClassCounter> classStats = new HashMap<String, ClassCounter>();
+
+ public JunitLikeXmlOutputListener(JUnitSystem system, File f) {
+ try {
+ writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8"));
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public void testRunStarted(Description description) throws Exception {
+ openElement(ROOT);
+ writeElement(DATE_ELEMENT, new Date().toString());
+ }
+
+ private void openElement(String name) throws IOException {
+ openElement(name, null);
+ }
+
+ private void openElement(String name, Map<String, String> atts) throws IOException {
+ StringBuilder attString = new StringBuilder();
+ if (atts != null) {
+ attString.append(" ");
+ Set<Entry<String, String>> entries = atts.entrySet();
+ for (Entry<String, String> entry : entries) {
+ attString.append(entry.getKey()).append("=\"").append(attributize(entry.getValue())).append("\"");
+ attString.append(" ");
+ }
+ }
+ writer.write("<" + name + attString.toString() + ">");
+ writer.newLine();
+ }
+
+ private static String attributize(String s) {
+ return s.replace("&", "&amp;").replace("<", "&lt;");
+ }
+
+ private void closeElement(String name) throws IOException {
+ writer.newLine();
+ writer.write("</" + name + ">");
+ writer.newLine();
+ }
+
+ private void writeContent(String content) throws IOException {
+ writer.write(CDATA_START + content + CDATA_END);
+ }
+
+ private void writeElement(String name, String content) throws IOException {
+ writeElement(name, content, null);
+ }
+
+ private void writeElement(String name, String content, Map<String, String> atts) throws IOException {
+ openElement(name, atts);
+ writeContent(content);
+ closeElement(name);
+ }
+
+ @Override
+ public void testStarted(Description description) throws Exception {
+ testFailed = null;
+ testStart = System.nanoTime()/1000l/1000l;
+ }
+
+ @Override
+ public void testFailure(Failure failure) throws IOException {
+ testFailed = failure;
+ }
+
+ @Override
+ public void testFinished(org.junit.runner.Description description) throws Exception {
+ long testTime = System.nanoTime()/1000l/1000l - testStart;
+ double testTimeSeconds = ((double) testTime) / 1000d;
+
+ Map<String, String> testcaseAtts = new HashMap<String, String>(3);
+ NumberFormat formatter = new DecimalFormat("#0.0000");
+ String stringedTime = formatter.format(testTimeSeconds);
+ stringedTime.replace(",", ".");
+ testcaseAtts.put(TEST_TIME_ATTRIBUTE, stringedTime);
+ testcaseAtts.put(TEST_CLASS_ATTRIBUTE, description.getClassName());
+ testcaseAtts.put(TEST_NAME_ATTRIBUTE, description.getMethodName());
+
+ openElement(TEST_ELEMENT, testcaseAtts);
+ if (testFailed != null) {
+ Map<String, String> errorAtts = new HashMap<String, String>(3);
+
+ errorAtts.put(ERROR_MESSAGE_ATTRIBUTE, testFailed.getMessage());
+ int i = testFailed.getTrace().indexOf(":");
+ if (i >= 0) {
+ errorAtts.put(ERROR_TYPE_ATTRIBUTE, testFailed.getTrace().substring(0, i));
+ } else {
+ errorAtts.put(ERROR_TYPE_ATTRIBUTE, "?");
+ }
+
+ writeElement(TEST_ERROR_ELEMENT, testFailed.getTrace(), errorAtts);
+ }
+
+ closeElement(TEST_ELEMENT);
+ writer.flush();
+
+ ClassCounter cc = classStats.get(description.getClassName());
+ if (cc == null) {
+ cc = new ClassCounter();
+ classStats.put(description.getClassName(), cc);
+ }
+ cc.total++;
+ cc.time += testTime;
+ if (testFailed == null) {
+ cc.passed++;
+ } else {
+
+ cc.failed++;
+ }
+ }
+
+ @Override
+ public void testRunFinished(Result result) throws Exception {
+
+ writeElement(SOUT_ELEMENT, "@sout@");
+ writeElement(SERR_ELEMENT, "@serr@");
+ openElement(STATS_ELEMENT);
+ openElement(SUMMARY_ELEMENT);
+ int passed = result.getRunCount() - result.getFailureCount() - result.getIgnoreCount();
+ int failed = result.getFailureCount();
+ int ignored = result.getIgnoreCount();
+ writeElement(SUMMARY_TOTAL_ELEMENT, String.valueOf(result.getRunCount()));
+ writeElement(SUMMARY_FAILED_ELEMENT, String.valueOf(failed));
+ writeElement(SUMMARY_IGNORED_ELEMENT, String.valueOf(ignored));
+ writeElement(SUMMARY_PASSED_ELEMENT, String.valueOf(passed));
+ closeElement(SUMMARY_ELEMENT);
+ openElement(CLASSES_ELEMENT);
+ Set<Entry<String, ClassCounter>> e = classStats.entrySet();
+ for (Entry<String, ClassCounter> entry : e) {
+
+ Map<String, String> testcaseAtts = new HashMap<String, String>(3);
+ testcaseAtts.put(TEST_NAME_ATTRIBUTE, entry.getKey());
+ testcaseAtts.put(TEST_TIME_ATTRIBUTE, String.valueOf(entry.getValue().time));
+
+ openElement(TEST_CLASS_ELEMENT, testcaseAtts);
+ writeElement(SUMMARY_PASSED_ELEMENT, String.valueOf(entry.getValue().passed));
+ writeElement(SUMMARY_FAILED_ELEMENT, String.valueOf(entry.getValue().failed));
+ writeElement(SUMMARY_IGNORED_ELEMENT, String.valueOf(entry.getValue().total - entry.getValue().failed - entry.getValue().passed));
+ writeElement(SUMMARY_TOTAL_ELEMENT, String.valueOf(entry.getValue().total));
+
+ closeElement(TEST_CLASS_ELEMENT);
+ }
+ closeElement(CLASSES_ELEMENT);
+ closeElement(STATS_ELEMENT);
+
+ closeElement(ROOT);
+ writer.flush();
+ writer.close();
+
+ }
+}