/* * Copyright (C) 2012 uPhyca Inc. * * Base on previous work by * Copyright (C) 2010 Diego Torres Milano * * Base on previous work by * Copyright (C) 2007 Hugo Visser * * Licensed 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.uphyca.testing; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlSerializer; import android.annotation.TargetApi; import android.os.Bundle; /** * This test runner creates an xml in the files directory of * the application under test. The output is compatible with * that of the junitreport ant task, the format that is * understood by Hudson. Currently this implementation does not * implement the all aspects of the junitreport format, but * enough for Hudson to parse the test results. */ public class JUnit4XMLInstrumentationTestRunner extends JUnit4InstrumentationTestRunner { private Writer mWriter; private XmlSerializer mTestSuiteSerializer; private long mTestStarted; /** * Output file name. */ private String mOutFileName; private static final String XML_OUT = "xml"; /** * Outfile argument name. * This argument can be passed to the instrumentation using <code>-e</code>. */ private static final String OUT_FILE_ARG = "outfile"; /** * Default output file name. */ private static final String OUT_FILE_DEFAULT = "test-results.xml"; private boolean getBooleanArgument(Bundle arguments, String tag) { String tagString = arguments.getString(tag); return tagString != null && Boolean.parseBoolean(tagString); } private boolean mXml; @Override public void onCreate(Bundle arguments) { if (arguments != null) { mXml = getBooleanArgument(arguments, XML_OUT); if (mXml) { mOutFileName = arguments.getString(OUT_FILE_ARG); if (mOutFileName == null) { mOutFileName = OUT_FILE_DEFAULT; } } } super.onCreate(arguments); } @TargetApi(8) @Override public void onStart() { if (mXml) { try { File dir = getTargetContext().getExternalFilesDir(null); if (dir == null) { dir = getTargetContext().getFilesDir(); } final File outFile = new File(dir, mOutFileName); startJUnitOutput(new FileWriter(outFile)); } catch (IOException e) { throw new RuntimeException(e); } } super.onStart(); } void startJUnitOutput(Writer writer) { try { mWriter = writer; mTestSuiteSerializer = newSerializer(mWriter); mTestSuiteSerializer.startDocument(null, null); mTestSuiteSerializer.startTag(null, "testsuites"); mTestSuiteSerializer.startTag(null, "testsuite"); } catch (Exception e) { throw new RuntimeException(e); } } private XmlSerializer newSerializer(Writer writer) { try { XmlPullParserFactory pf = XmlPullParserFactory.newInstance(); XmlSerializer serializer = pf.newSerializer(); serializer.setOutput(writer); return serializer; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void sendStatus(int resultCode, Bundle results) { super.sendStatus(resultCode, results); if (!mXml) { return; } switch (resultCode) { case REPORT_VALUE_RESULT_ERROR: case REPORT_VALUE_RESULT_FAILURE: case REPORT_VALUE_RESULT_OK: try { recordTestResult(resultCode, results); } catch (IOException e) { throw new RuntimeException(e); } break; case REPORT_VALUE_RESULT_START: recordTestStart(results); default: break; } } void recordTestStart(Bundle results) { mTestStarted = System.currentTimeMillis(); } void recordTestResult(int resultCode, Bundle results) throws IOException { float time = (System.currentTimeMillis() - mTestStarted) / 1000.0f; String className = results.getString(REPORT_KEY_NAME_CLASS); String testMethod = results.getString(REPORT_KEY_NAME_TEST); String stack = results.getString(REPORT_KEY_STACK); int current = results.getInt(REPORT_KEY_NUM_CURRENT); int total = results.getInt(REPORT_KEY_NUM_TOTAL); mTestSuiteSerializer.startTag(null, "testcase"); mTestSuiteSerializer.attribute(null, "classname", className); mTestSuiteSerializer.attribute(null, "name", testMethod); if (resultCode != REPORT_VALUE_RESULT_OK) { mTestSuiteSerializer.startTag(null, "failure"); if (stack != null) { String reason = stack.substring(0, stack.indexOf('\n')); String message = ""; int index = reason.indexOf(':'); if (index > -1) { message = reason.substring(index + 1); reason = reason.substring(0, index); } mTestSuiteSerializer.attribute(null, "message", message); mTestSuiteSerializer.attribute(null, "type", reason); mTestSuiteSerializer.text(stack); } mTestSuiteSerializer.endTag(null, "failure"); } else { mTestSuiteSerializer.attribute(null, "time", String.format("%.3f", time)); } mTestSuiteSerializer.endTag(null, "testcase"); if (current == total) { mTestSuiteSerializer.startTag(null, "system-out"); mTestSuiteSerializer.endTag(null, "system-out"); mTestSuiteSerializer.startTag(null, "system-err"); mTestSuiteSerializer.endTag(null, "system-err"); mTestSuiteSerializer.endTag(null, "testsuite"); mTestSuiteSerializer.flush(); } } @Override public void finish(int resultCode, Bundle results) { if (mXml) { endTestSuites(); } super.finish(resultCode, results); } void endTestSuites() { try { if (mTestSuiteSerializer != null) { mTestSuiteSerializer.endTag(null, "testsuites"); mTestSuiteSerializer.endDocument(); mTestSuiteSerializer.flush(); } if (mWriter != null) { mWriter.flush(); mWriter.close(); } } catch (IOException e) { throw new RuntimeException(e); } } }