aboutsummaryrefslogtreecommitdiffstats
path: root/ddms/libs
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-18 17:39:43 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-18 17:39:43 -0700
commit1c8fdff64e5cb89e687925812ea0b372e0db72bc (patch)
tree5b0e5b72314ac2074529b06b06026723edee8a57 /ddms/libs
parent763ca7d5803d8088e192f4d226ff9e96820c7ace (diff)
downloadsdk-1c8fdff64e5cb89e687925812ea0b372e0db72bc.zip
sdk-1c8fdff64e5cb89e687925812ea0b372e0db72bc.tar.gz
sdk-1c8fdff64e5cb89e687925812ea0b372e0db72bc.tar.bz2
auto import from //branches/cupcake_rel/...@140373
Diffstat (limited to 'ddms/libs')
-rwxr-xr-xddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java82
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java148
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java51
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java21
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
}
-
}
}