diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-18 17:39:43 -0700 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-18 17:39:43 -0700 |
| commit | 1c8fdff64e5cb89e687925812ea0b372e0db72bc (patch) | |
| tree | 5b0e5b72314ac2074529b06b06026723edee8a57 /ddms/libs | |
| parent | 763ca7d5803d8088e192f4d226ff9e96820c7ace (diff) | |
| download | sdk-1c8fdff64e5cb89e687925812ea0b372e0db72bc.zip sdk-1c8fdff64e5cb89e687925812ea0b372e0db72bc.tar.gz sdk-1c8fdff64e5cb89e687925812ea0b372e0db72bc.tar.bz2 | |
auto import from //branches/cupcake_rel/...@140373
Diffstat (limited to 'ddms/libs')
4 files changed, 198 insertions, 104 deletions
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java index bc1834f..1a0b21f 100755 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java @@ -27,7 +27,14 @@ import com.android.ddmlib.MultiLineReceiver; * <p>Expects the following output: * * <p>If fatal error occurred when attempted to run the tests: - * <pre> INSTRUMENTATION_FAILED: </pre> + * <pre> + * INSTRUMENTATION_STATUS: Error=error Message + * INSTRUMENTATION_FAILED: + * </pre> + * <p>or + * <pre> + * INSTRUMENTATION_RESULT: shortMsg=error Message + * </pre> * * <p>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 @@ -56,6 +63,8 @@ public class InstrumentationResultParser extends MultiLineReceiver { private static final String CLASS = "class"; private static final String STACK = "stack"; private static final String NUMTESTS = "numtests"; + private static final String ERROR = "Error"; + private static final String SHORTMSG = "shortMsg"; } /** Test result status codes. */ @@ -71,6 +80,8 @@ public class InstrumentationResultParser extends MultiLineReceiver { private static final String STATUS = "INSTRUMENTATION_STATUS: "; private static final String STATUS_CODE = "INSTRUMENTATION_STATUS_CODE: "; private static final String STATUS_FAILED = "INSTRUMENTATION_FAILED: "; + private static final String CODE = "INSTRUMENTATION_CODE: "; + private static final String RESULT = "INSTRUMENTATION_RESULT: "; private static final String TIME_REPORT = "Time: "; } @@ -90,6 +101,23 @@ public class InstrumentationResultParser extends MultiLineReceiver { boolean isComplete() { return mCode != null && mTestName != null && mTestClass != null; } + + /** Provides a more user readable string for TestResult, if possible */ + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + if (mTestClass != null ) { + output.append(mTestClass); + output.append('#'); + } + if (mTestName != null) { + output.append(mTestName); + } + if (output.length() > 0) { + return output.toString(); + } + return "unknown result"; + } } /** Stores the status values for the test result currently being parsed */ @@ -130,6 +158,8 @@ public class InstrumentationResultParser extends MultiLineReceiver { public void processNewLines(String[] lines) { for (String line : lines) { parse(line); + // in verbose mode, dump all adb output to log + Log.v(LOG_TAG, line); } } @@ -160,9 +190,15 @@ public class InstrumentationResultParser extends MultiLineReceiver { // Previous status key-value has been collected. Store it. submitCurrentKeyValue(); parseKey(line, Prefixes.STATUS.length()); - } else if (line.startsWith(Prefixes.STATUS_FAILED)) { - Log.e(LOG_TAG, "test run failed " + line); - mTestListener.testRunFailed(line); + } 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 { @@ -186,19 +222,19 @@ public class InstrumentationResultParser extends MultiLineReceiver { if (mCurrentKey.equals(StatusKeys.CLASS)) { testInfo.mTestClass = statusValue.trim(); - } - else if (mCurrentKey.equals(StatusKeys.TEST)) { + } else if (mCurrentKey.equals(StatusKeys.TEST)) { testInfo.mTestName = statusValue.trim(); - } - else if (mCurrentKey.equals(StatusKeys.NUMTESTS)) { + } else if (mCurrentKey.equals(StatusKeys.NUMTESTS)) { try { testInfo.mNumTests = Integer.parseInt(statusValue); - } - catch (NumberFormatException e) { + } catch (NumberFormatException e) { Log.e(LOG_TAG, "Unexpected integer number of tests, received " + statusValue); } - } - else if (mCurrentKey.equals(StatusKeys.STACK)) { + } 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; } @@ -229,7 +265,7 @@ public class InstrumentationResultParser extends MultiLineReceiver { int endKeyPos = line.indexOf('=', keyStartPos); if (endKeyPos != -1) { mCurrentKey = line.substring(keyStartPos, endKeyPos).trim(); - parseValue(line, endKeyPos+1); + parseValue(line, endKeyPos + 1); } } @@ -252,8 +288,7 @@ public class InstrumentationResultParser extends MultiLineReceiver { TestResult testInfo = getCurrentTestInfo(); try { testInfo.mCode = Integer.parseInt(value); - } - catch (NumberFormatException e) { + } catch (NumberFormatException e) { Log.e(LOG_TAG, "Expected integer status code, received: " + value); } @@ -286,7 +321,7 @@ public class InstrumentationResultParser extends MultiLineReceiver { */ private void reportResult(TestResult testInfo) { if (!testInfo.isComplete()) { - Log.e(LOG_TAG, "invalid instrumentation status bundle " + testInfo.toString()); + Log.w(LOG_TAG, "invalid instrumentation status bundle " + testInfo.toString()); return; } reportTestRunStarted(testInfo); @@ -337,8 +372,7 @@ public class InstrumentationResultParser extends MultiLineReceiver { private String getTrace(TestResult testInfo) { if (testInfo.mStackTrace != null) { return testInfo.mStackTrace; - } - else { + } else { Log.e(LOG_TAG, "Could not find stack trace for failed test "); return new Throwable("Unknown failure").toString(); } @@ -351,14 +385,20 @@ public class InstrumentationResultParser extends MultiLineReceiver { String timeString = line.substring(startPos); try { float timeSeconds = Float.parseFloat(timeString); - mTestTime = (long)(timeSeconds * 1000); - } - catch (NumberFormatException e) { + mTestTime = (long) (timeSeconds * 1000); + } catch (NumberFormatException e) { Log.e(LOG_TAG, "Unexpected time format " + timeString); } } /** + * Process a instrumentation run failure + */ + private void handleTestRunFailed(String errorMsg) { + mTestListener.testRunFailed(errorMsg == null ? "Unknown error" : errorMsg); + } + + /** * Called by parent when adb session is complete. */ @Override diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java index 4edbbbb..9995426 100644 --- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java +++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java @@ -21,27 +21,35 @@ import com.android.ddmlib.IDevice; import com.android.ddmlib.Log; import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; +import java.util.Map.Entry; /** * Runs a Android test command remotely and reports results. */ public class RemoteAndroidTestRunner { - private static final char CLASS_SEPARATOR = ','; - private static final char METHOD_SEPARATOR = '#'; - private static final char RUNNER_SEPARATOR = '/'; - private String mClassArg; private final String mPackageName; private final String mRunnerName; - private String mExtraArgs; - private boolean mLogOnlyMode; private IDevice mRemoteDevice; + /** map of name-value instrumentation argument pairs */ + private Map<String, String> mArgMap; private InstrumentationResultParser mParser; private static final String LOG_TAG = "RemoteAndroidTest"; - private static final String DEFAULT_RUNNER_NAME = - "android.test.InstrumentationTestRunner"; - + private static final String DEFAULT_RUNNER_NAME = "android.test.InstrumentationTestRunner"; + + private static final char CLASS_SEPARATOR = ','; + private static final char METHOD_SEPARATOR = '#'; + private static final char RUNNER_SEPARATOR = '/'; + + // defined instrumentation argument names + private static final String CLASS_ARG_NAME = "class"; + private static final String LOG_ARG_NAME = "log"; + private static final String DEBUG_ARG_NAME = "debug"; + private static final String COVERAGE_ARG_NAME = "coverage"; + /** * Creates a remote Android test runner. * @@ -56,12 +64,10 @@ public class RemoteAndroidTestRunner { mPackageName = packageName; mRunnerName = runnerName; - mRemoteDevice = remoteDevice; - mClassArg = null; - mExtraArgs = ""; - mLogOnlyMode = false; + mRemoteDevice = remoteDevice; + mArgMap = new Hashtable<String, String>(); } - + /** * Alternate constructor. Uses default instrumentation runner. * @@ -72,7 +78,7 @@ public class RemoteAndroidTestRunner { IDevice remoteDevice) { this(packageName, null, remoteDevice); } - + /** * Returns the application package name. */ @@ -89,14 +95,14 @@ public class RemoteAndroidTestRunner { } return mRunnerName; } - + /** * Returns the complete instrumentation component path. */ private String getRunnerPath() { return getPackageName() + RUNNER_SEPARATOR + getRunnerName(); } - + /** * Sets to run only tests in this class * Must be called before 'run'. @@ -104,7 +110,7 @@ public class RemoteAndroidTestRunner { * @param className fully qualified class name (eg x.y.z) */ public void setClassName(String className) { - mClassArg = className; + addInstrumentationArg(CLASS_ARG_NAME, className); } /** @@ -119,15 +125,15 @@ public class RemoteAndroidTestRunner { public void setClassNames(String[] classNames) { StringBuilder classArgBuilder = new StringBuilder(); - for (int i=0; i < classNames.length; i++) { + for (int i = 0; i < classNames.length; i++) { if (i != 0) { classArgBuilder.append(CLASS_SEPARATOR); } classArgBuilder.append(classNames[i]); } - mClassArg = classArgBuilder.toString(); + setClassName(classArgBuilder.toString()); } - + /** * Sets to run only specified test method * Must be called before 'run'. @@ -136,47 +142,70 @@ public class RemoteAndroidTestRunner { * @param testName method name */ public void setMethodName(String className, String testName) { - mClassArg = className + METHOD_SEPARATOR + testName; + setClassName(className + METHOD_SEPARATOR + testName); } - + /** - * Sets extra arguments to include in instrumentation command. - * Must be called before 'run'. + * Adds a argument to include in instrumentation command. + * <p/> + * Must be called before 'run'. If an argument with given name has already been provided, it's + * value will be overridden. * - * @param instrumentationArgs must not be null + * @param name the name of the instrumentation bundle argument + * @param value the value of the argument */ - public void setExtraArgs(String instrumentationArgs) { - if (instrumentationArgs == null) { - throw new IllegalArgumentException("instrumentationArgs cannot be null"); + public void addInstrumentationArg(String name, String value) { + if (name == null || value == null) { + throw new IllegalArgumentException("name or value arguments cannot be null"); } - mExtraArgs = instrumentationArgs; + mArgMap.put(name, value); } - + /** - * Returns the extra instrumentation arguments. + * Adds a boolean argument to include in instrumentation command. + * <p/> + * @see RemoteAndroidTestRunner#addInstrumentationArg + * + * @param name the name of the instrumentation bundle argument + * @param value the value of the argument */ - public String getExtraArgs() { - return mExtraArgs; + public void addBooleanArg(String name, boolean value) { + addInstrumentationArg(name, Boolean.toString(value)); } - + /** * Sets this test run to log only mode - skips test execution. */ public void setLogOnly(boolean logOnly) { - mLogOnlyMode = logOnly; + addBooleanArg(LOG_ARG_NAME, logOnly); } - + + /** + * Sets this debug mode of this test run. If true, the Android test runner will wait for a + * debugger to attach before proceeding with test execution. + */ + public void setDebug(boolean debug) { + addBooleanArg(DEBUG_ARG_NAME, debug); + } + + /** + * Sets this code coverage mode of this test run. + */ + public void setCoverage(boolean coverage) { + addBooleanArg(COVERAGE_ARG_NAME, coverage); + } + /** * Execute this test run. * * @param listener listens for test results */ public void run(ITestRunListener listener) { - final String runCaseCommandStr = "am instrument -w -r " - + getClassCmd() + " " + getLogCmd() + " " + getExtraArgs() + " " + getRunnerPath(); + final String runCaseCommandStr = String.format("am instrument -w -r %s %s", + getArgsCommand(), getRunnerPath()); Log.d(LOG_TAG, runCaseCommandStr); mParser = new InstrumentationResultParser(listener); - + try { mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser); } catch (IOException e) { @@ -184,7 +213,7 @@ public class RemoteAndroidTestRunner { listener.testRunFailed(e.toString()); } } - + /** * Requests cancellation of this test run. */ @@ -193,36 +222,19 @@ public class RemoteAndroidTestRunner { mParser.cancel(); } } - - /** - * Returns the test class argument. - */ - private String getClassArg() { - return mClassArg; - } - - /** - * Returns the full instrumentation command which specifies the test classes to execute. - * Returns an empty string if no classes were specified. - */ - private String getClassCmd() { - String classArg = getClassArg(); - if (classArg != null) { - return "-e class " + classArg; - } - return ""; - } /** - * Returns the full command to enable log only mode - if specified. Otherwise returns an - * empty string. + * Returns the full instrumentation command line syntax for the provided instrumentation + * arguments. + * Returns an empty string if no arguments were specified. */ - private String getLogCmd() { - if (mLogOnlyMode) { - return "-e log true"; - } - else { - return ""; + private String getArgsCommand() { + StringBuilder commandBuilder = new StringBuilder(); + for (Entry<String, String> argPair : mArgMap.entrySet()) { + final String argCmd = String.format(" -e %s %s", argPair.getKey(), + argPair.getValue()); + commandBuilder.append(argCmd); } + return commandBuilder.toString(); } } diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java index 77d10c1..7742dd6 100644 --- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java +++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java @@ -103,9 +103,43 @@ public class InstrumentationResultParserTest extends TestCase { injectTestString(timeString); assertEquals(4900, mTestResult.mTestTime); } + + /** + * Test basic parsing of a test run failure. + */ + public void testRunFailed() { + StringBuilder output = new StringBuilder(); + final String errorMessage = "Unable to find instrumentation info"; + addStatusKey(output, "Error", errorMessage); + addStatusCode(output, "-1"); + output.append("INSTRUMENTATION_FAILED: com.dummy/android.test.InstrumentationTestRunner"); + addLineBreak(output); + + injectTestString(output.toString()); + + assertEquals(errorMessage, mTestResult.mRunFailedMessage); + } + + /** + * Test parsing of a test run failure, where an instrumentation component failed to load + * Parsing input takes the from of INSTRUMENTATION_RESULT: fff + */ + public void testRunFailedResult() { + StringBuilder output = new StringBuilder(); + final String errorMessage = "Unable to instantiate instrumentation"; + output.append("INSTRUMENTATION_RESULT: shortMsg="); + output.append(errorMessage); + addLineBreak(output); + output.append("INSTRUMENTATION_CODE: 0"); + addLineBreak(output); + + injectTestString(output.toString()); + + assertEquals(errorMessage, mTestResult.mRunFailedMessage); + } /** - * builds a common test result using TEST_NAME and TEST_CLASS. + * Builds a common test result using TEST_NAME and TEST_CLASS. */ private StringBuilder buildCommonResult() { StringBuilder output = new StringBuilder(); @@ -146,6 +180,13 @@ public class InstrumentationResultParserTest extends TestCase { outputBuilder.append(key); outputBuilder.append('='); outputBuilder.append(value); + addLineBreak(outputBuilder); + } + + /** + * Append line break characters to output + */ + private void addLineBreak(StringBuilder outputBuilder) { outputBuilder.append("\r\n"); } @@ -164,7 +205,7 @@ public class InstrumentationResultParserTest extends TestCase { private void addStatusCode(StringBuilder outputBuilder, String value) { outputBuilder.append("INSTRUMENTATION_STATUS_CODE: "); outputBuilder.append(value); - outputBuilder.append("\r\n"); + addLineBreak(outputBuilder); } /** @@ -197,11 +238,14 @@ public class InstrumentationResultParserTest extends TestCase { TestFailure mTestStatus; String mTrace; boolean mStopped; + /** stores the error message provided to testRunFailed */ + String mRunFailedMessage; VerifyingTestResult() { mNumTestsRun = 0; mTestStatus = null; mStopped = false; + mRunFailedMessage = null; } public void testEnded(TestIdentifier test) { @@ -238,8 +282,7 @@ public class InstrumentationResultParserTest extends TestCase { } public void testRunFailed(String errorMessage) { - // ignored + mRunFailedMessage = errorMessage; } } - } diff --git a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java index 9acaaf9..6a653ad 100644 --- a/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java +++ b/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java @@ -17,18 +17,17 @@ package com.android.ddmlib.testrunner; import com.android.ddmlib.Client; +import com.android.ddmlib.Device.DeviceState; import com.android.ddmlib.FileListingService; import com.android.ddmlib.IDevice; import com.android.ddmlib.IShellOutputReceiver; +import com.android.ddmlib.log.LogReceiver; import com.android.ddmlib.RawImage; import com.android.ddmlib.SyncService; -import com.android.ddmlib.Device.DeviceState; -import com.android.ddmlib.log.LogReceiver; - -import junit.framework.TestCase; import java.io.IOException; import java.util.Map; +import junit.framework.TestCase; /** * Tests RemoteAndroidTestRunner. @@ -82,14 +81,15 @@ public class RemoteAndroidTestRunnerTest extends TestCase { } /** - * Test the building of the instrumentation runner command with extra args set. + * Test the building of the instrumentation runner command with extra argument added. */ - public void testRunWithExtraArgs() { - final String extraArgs = "blah"; - mRunner.setExtraArgs(extraArgs); + public void testRunWithAddInstrumentationArg() { + final String extraArgName = "blah"; + final String extraArgValue = "blahValue"; + mRunner.addInstrumentationArg(extraArgName, extraArgValue); mRunner.run(new EmptyListener()); - assertStringsEquals(String.format("am instrument -w -r %s %s/%s", extraArgs, - TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand()); + assertStringsEquals(String.format("am instrument -w -r -e %s %s %s/%s", extraArgName, + extraArgValue, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand()); } @@ -243,6 +243,5 @@ public class RemoteAndroidTestRunnerTest extends TestCase { public void testStarted(TestIdentifier test) { // ignore } - } } |
