FileDocCategorySizeDatePackage
InstrumentationResultParser.javaAPI DocAndroid 1.5 API14498Wed May 06 22:41:08 BST 2009com.android.ddmlib.testrunner

InstrumentationResultParser

public class InstrumentationResultParser extends com.android.ddmlib.MultiLineReceiver
Parses the 'raw output mode' results of an instrumentation test run from shell and informs a ITestRunListener of the results.

Expects the following output:

If fatal error occurred when attempted to run the tests:

INSTRUMENTATION_STATUS: Error=error Message
INSTRUMENTATION_FAILED:

or

INSTRUMENTATION_RESULT: shortMsg=error Message

Otherwise, expect a series of test results, each one containing a set of status key/value pairs, delimited by a start(1)/pass(0)/fail(-2)/error(-1) status code result. At end of test run, expects that the elapsed test time in seconds will be displayed

For example:

INSTRUMENTATION_STATUS_CODE: 1
INSTRUMENTATION_STATUS: class=com.foo.FooTest
INSTRUMENTATION_STATUS: test=testFoo
INSTRUMENTATION_STATUS: numtests=2
INSTRUMENTATION_STATUS: stack=com.foo.FooTest#testFoo:312
com.foo.X
INSTRUMENTATION_STATUS_CODE: -2
...

Time: X

Note that the "value" portion of the key-value pair may wrap over several text lines

Fields Summary
private final ITestRunListener
mTestListener
private TestResult
mCurrentTestResult
Stores the status values for the test result currently being parsed
private String
mCurrentKey
Stores the current "key" portion of the status key-value being parsed.
private StringBuilder
mCurrentValue
Stores the current "value" portion of the status key-value being parsed.
private boolean
mTestStartReported
True if start of test has already been reported to listener.
private long
mTestTime
The elapsed time of the test run, in milliseconds.
private boolean
mIsCancelled
True if current test run has been canceled by user.
private static final String
LOG_TAG
Constructors Summary
public InstrumentationResultParser(ITestRunListener listener)
Creates the InstrumentationResultParser.

param
listener informed of test results as the tests are executing

    
                        
       
        mTestListener = listener;
    
Methods Summary
public voidcancel()
Requests cancellation of test run.

        mIsCancelled = true;
    
private voidclearCurrentTestInfo()

        mCurrentTestResult = null;
    
public voiddone()
Called by parent when adb session is complete.

        super.done();
        mTestListener.testRunEnded(mTestTime);
    
private com.android.ddmlib.testrunner.InstrumentationResultParser$TestResultgetCurrentTestInfo()

        if (mCurrentTestResult == null) {
            mCurrentTestResult = new TestResult();
        }
        return mCurrentTestResult;
    
private java.lang.StringgetTrace(com.android.ddmlib.testrunner.InstrumentationResultParser$TestResult testInfo)
Returns the stack trace of the current failed test, from the provided testInfo.

        if (testInfo.mStackTrace != null) {
            return testInfo.mStackTrace;    
        } else {
            Log.e(LOG_TAG, "Could not find stack trace for failed test ");
            return new Throwable("Unknown failure").toString();
        }
    
private voidhandleTestRunFailed(java.lang.String errorMsg)
Process a instrumentation run failure

        mTestListener.testRunFailed(errorMsg == null ? "Unknown error" : errorMsg);
    
public booleanisCancelled()
Returns true if test run canceled.

see
IShellOutputReceiver#isCancelled()

        return mIsCancelled;
    
private voidparse(java.lang.String line)
Parse an individual output line. Expects a line that is one of:
  • The start of a new status line (starts with Prefixes.STATUS or Prefixes.STATUS_CODE), and thus there is a new key=value pair to parse, and the previous key-value pair is finished.
  • A continuation of the previous status (the "value" portion of the key has wrapped to the next line).
  • A line reporting a fatal error in the test run (Prefixes.STATUS_FAILED)
  • A line reporting the total elapsed time of the test run. (Prefixes.TIME_REPORT)

param
line Text output line

        if (line.startsWith(Prefixes.STATUS_CODE)) {
            // Previous status key-value has been collected. Store it.
            submitCurrentKeyValue();
            parseStatusCode(line);
        } else if (line.startsWith(Prefixes.STATUS)) {
            // Previous status key-value has been collected. Store it.
            submitCurrentKeyValue();
            parseKey(line, Prefixes.STATUS.length());
        } else if (line.startsWith(Prefixes.RESULT)) {
            // Previous status key-value has been collected. Store it.
            submitCurrentKeyValue();
            parseKey(line, Prefixes.RESULT.length());  
        } else if (line.startsWith(Prefixes.STATUS_FAILED) || 
                   line.startsWith(Prefixes.CODE)) {
            // Previous status key-value has been collected. Store it.
            submitCurrentKeyValue();
            // just ignore the remaining data on this line            
        } else if (line.startsWith(Prefixes.TIME_REPORT)) {
            parseTime(line, Prefixes.TIME_REPORT.length());
        } else {
            if (mCurrentValue != null) {
                // this is a value that has wrapped to next line. 
                mCurrentValue.append("\r\n");
                mCurrentValue.append(line);
            } else {
                Log.w(LOG_TAG, "unrecognized line " + line);
            }
        }
    
private voidparseKey(java.lang.String line, int keyStartPos)
Parses the key from the current line. Expects format of "key=value".

param
line full line of text to parse
param
keyStartPos the starting position of the key in the given line

        int endKeyPos = line.indexOf('=", keyStartPos);
        if (endKeyPos != -1) {
            mCurrentKey = line.substring(keyStartPos, endKeyPos).trim();
            parseValue(line, endKeyPos + 1);
        }
    
private voidparseStatusCode(java.lang.String line)
Parses out a status code result.

        String value = line.substring(Prefixes.STATUS_CODE.length()).trim();
        TestResult testInfo = getCurrentTestInfo();
        try {
            testInfo.mCode = Integer.parseInt(value);    
        } catch (NumberFormatException e) {
            Log.e(LOG_TAG, "Expected integer status code, received: " + value);
        }
        
        // this means we're done with current test result bundle
        reportResult(testInfo);
        clearCurrentTestInfo();
    
private voidparseTime(java.lang.String line, int startPos)
Parses out and store the elapsed time.

        String timeString = line.substring(startPos);
        try {
            float timeSeconds = Float.parseFloat(timeString);
            mTestTime = (long) (timeSeconds * 1000); 
        } catch (NumberFormatException e) {
            Log.e(LOG_TAG, "Unexpected time format " + timeString);
        }
    
private voidparseValue(java.lang.String line, int valueStartPos)
Parses the start of a key=value pair.

param
line - full line of text to parse
param
valueStartPos - the starting position of the value in the given line

        mCurrentValue = new StringBuilder();
        mCurrentValue.append(line.substring(valueStartPos));
    
public voidprocessNewLines(java.lang.String[] lines)
Processes the instrumentation test output from shell.

see
MultiLineReceiver#processNewLines

        for (String line : lines) {
            parse(line);
            // in verbose mode, dump all adb output to log
            Log.v(LOG_TAG, line);
        }
    
private voidreportResult(com.android.ddmlib.testrunner.InstrumentationResultParser$TestResult testInfo)
Reports a test result to the test run listener. Must be called when a individual test result has been fully parsed.

param
statusMap key-value status pairs of test result

        if (!testInfo.isComplete()) {
            Log.w(LOG_TAG, "invalid instrumentation status bundle " + testInfo.toString());
            return;
        }
        reportTestRunStarted(testInfo);
        TestIdentifier testId = new TestIdentifier(testInfo.mTestClass, testInfo.mTestName);

        switch (testInfo.mCode) {
            case StatusCodes.START:
                mTestListener.testStarted(testId);
                break;
            case StatusCodes.FAILURE:
                mTestListener.testFailed(ITestRunListener.TestFailure.FAILURE, testId, 
                        getTrace(testInfo));
                mTestListener.testEnded(testId);
                break;
            case StatusCodes.ERROR:
                mTestListener.testFailed(ITestRunListener.TestFailure.ERROR, testId, 
                        getTrace(testInfo));
                mTestListener.testEnded(testId);
                break;
            case StatusCodes.OK:
                mTestListener.testEnded(testId);
                break;
            default:
                Log.e(LOG_TAG, "Unknown status code received: " + testInfo.mCode);
                mTestListener.testEnded(testId);
            break;
        }

    
private voidreportTestRunStarted(com.android.ddmlib.testrunner.InstrumentationResultParser$TestResult testInfo)
Reports the start of a test run, and the total test count, if it has not been previously reported.

param
testInfo current test status values

        // if start test run not reported yet
        if (!mTestStartReported && testInfo.mNumTests != null) {
            mTestListener.testRunStarted(testInfo.mNumTests);
            mTestStartReported = true;
        }
    
private voidsubmitCurrentKeyValue()
Stores the currently parsed key-value pair into mCurrentTestInfo.

        if (mCurrentKey != null && mCurrentValue != null) {
            TestResult testInfo = getCurrentTestInfo();
            String statusValue = mCurrentValue.toString();

            if (mCurrentKey.equals(StatusKeys.CLASS)) {
                testInfo.mTestClass = statusValue.trim();
            } else if (mCurrentKey.equals(StatusKeys.TEST)) {
                testInfo.mTestName = statusValue.trim();
            } else if (mCurrentKey.equals(StatusKeys.NUMTESTS)) {
                try {
                    testInfo.mNumTests = Integer.parseInt(statusValue);
                } catch (NumberFormatException e) {
                    Log.e(LOG_TAG, "Unexpected integer number of tests, received " + statusValue);
                }
            } else if (mCurrentKey.equals(StatusKeys.ERROR) || 
                    mCurrentKey.equals(StatusKeys.SHORTMSG)) {
                // test run must have failed
                handleTestRunFailed(statusValue); 
            } else if (mCurrentKey.equals(StatusKeys.STACK)) {
                testInfo.mStackTrace = statusValue;
            }

            mCurrentKey = null;
            mCurrentValue = null;
        }