aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrett Chabot <brettchabot@android.com>2010-09-30 11:28:39 -0700
committerAndroid Code Review <code-review@android.com>2010-09-30 11:28:39 -0700
commitb1619f1e1fe0590b7d57fa63823a0d4d3464338f (patch)
tree3302ff23c0d927409292d96c529f5c6958c1cf17
parentad5d6730be32ab38be880513502fc2fea0512c1b (diff)
parentfa063dab862bf76595739580f87c7bf803c42e43 (diff)
downloadsdk-b1619f1e1fe0590b7d57fa63823a0d4d3464338f.zip
sdk-b1619f1e1fe0590b7d57fa63823a0d4d3464338f.tar.gz
sdk-b1619f1e1fe0590b7d57fa63823a0d4d3464338f.tar.bz2
Merge "Fix ddmlib test run failure and metric reporting."
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java76
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java44
-rw-r--r--ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java28
-rw-r--r--ddms/libs/ddmlib/tests/.classpath1
-rw-r--r--ddms/libs/ddmlib/tests/Android.mk4
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java302
-rw-r--r--ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java290
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties2
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF3
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.tests/build.properties3
10 files changed, 301 insertions, 452 deletions
diff --git a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
index b235597..a8b117d 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
@@ -19,8 +19,20 @@ package com.android.ddmlib.testrunner;
import java.util.Map;
/**
- * Receives event notifications during instrumentation test runs.
+ * Receives event notifications during instrumentation test runs.
+ * <p/>
* Patterned after {@link junit.runner.TestRunListener}.
+ * <p/>
+ * The sequence of calls will be:
+ * <ul>
+ * <li> testRunStarted
+ * <li> testStarted
+ * <li> [testFailed]
+ * <li> testEnded
+ * <li> ....
+ * <li> [testRunFailed]
+ * <li> testRunEnded
+ * </ul>
*/
public interface ITestRunListener {
@@ -34,7 +46,7 @@ public interface ITestRunListener {
FAILURE
}
- /**
+ /**
* Reports the start of a test run.
*
* @param runName the test run name
@@ -43,49 +55,55 @@ public interface ITestRunListener {
public void testRunStarted(String runName, int testCount);
/**
- * Reports end of test run.
- *
- * @param elapsedTime device reported elapsed time, in milliseconds
- * @param runMetrics key-value pairs reported at the end of a test run
- */
- public void testRunEnded(long elapsedTime, Map<String, String> runMetrics);
-
- /**
- * Reports test run stopped before completion.
- *
- * @param elapsedTime device reported elapsed time, in milliseconds
+ * Reports the start of an individual test case.
+ *
+ * @param test identifies the test
*/
- public void testRunStopped(long elapsedTime);
+ public void testStarted(TestIdentifier test);
/**
- * Reports the start of an individual test case.
- *
+ * Reports the failure of a individual test case.
+ * <p/>
+ * Will be called between testStarted and testEnded.
+ *
+ * @param status failure type
* @param test identifies the test
+ * @param trace stack trace of failure
*/
- public void testStarted(TestIdentifier test);
+ public void testFailed(TestFailure status, TestIdentifier test, String trace);
/**
* Reports the execution end of an individual test case.
+ * <p/>
* If {@link #testFailed} was not invoked, this test passed. Also returns any key/value
* metrics which may have been emitted during the test case's execution.
- *
+ *
* @param test identifies the test
* @param testMetrics a {@link Map} of the metrics emitted
*/
public void testEnded(TestIdentifier test, Map<String, String> testMetrics);
/**
- * Reports the failure of a individual test case.
- * Will be called between testStarted and testEnded.
- *
- * @param status failure type
- * @param test identifies the test
- * @param trace stack trace of failure
- */
- public void testFailed(TestFailure status, TestIdentifier test, String trace);
-
- /**
- * Reports test run failed to execute due to a fatal error.
+ * Reports test run failed to complete due to a fatal error.
+ *
+ * @param errorMessage {@link String} describing reason for run failure.
*/
public void testRunFailed(String errorMessage);
+
+ /**
+ * Reports test run stopped before completion due to a user request.
+ * <p/>
+ * TODO: currently unused, consider removing
+ *
+ * @param elapsedTime device reported elapsed time, in milliseconds
+ */
+ public void testRunStopped(long elapsedTime);
+
+ /**
+ * Reports end of test run.
+ *
+ * @param elapsedTime device reported elapsed time, in milliseconds
+ * @param runMetrics key-value pairs reported at the end of a test run
+ */
+ public void testRunEnded(long elapsedTime, Map<String, String> runMetrics);
}
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 61236d8..4819fbc 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
@@ -23,7 +23,9 @@ import com.android.ddmlib.MultiLineReceiver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -74,6 +76,21 @@ public class InstrumentationResultParser extends MultiLineReceiver {
private static final String SHORTMSG = "shortMsg";
}
+ /** The set of expected status keys. Used to filter which keys should be stored as metrics */
+ private static final Set<String> KNOWN_KEYS = new HashSet<String>();
+ static {
+ KNOWN_KEYS.add(StatusKeys.TEST);
+ KNOWN_KEYS.add(StatusKeys.CLASS);
+ KNOWN_KEYS.add(StatusKeys.STACK);
+ KNOWN_KEYS.add(StatusKeys.NUMTESTS);
+ KNOWN_KEYS.add(StatusKeys.ERROR);
+ KNOWN_KEYS.add(StatusKeys.SHORTMSG);
+ // unused, but regularly occurring status keys.
+ KNOWN_KEYS.add("stream");
+ KNOWN_KEYS.add("id");
+ KNOWN_KEYS.add("current");
+ }
+
/** Test result status codes. */
private static class StatusCodes {
private static final int FAILURE = -2;
@@ -183,6 +200,12 @@ public class InstrumentationResultParser extends MultiLineReceiver {
/** Error message supplied when no parseable test results are received from test run. */
static final String NO_TEST_RESULTS_MSG = "No test results";
+ /** Error message supplied when a test start bundle is parsed, but not the test end bundle. */
+ static final String INCOMPLETE_TEST_ERR_MSG_PREFIX = "Incomplete";
+
+ /** Error message supplied when the test run is incomplete. */
+ static final String INCOMPLETE_RUN_ERR_MSG_PREFIX = "Test run incomplete";
+
/**
* Creates the InstrumentationResultParser.
*
@@ -284,8 +307,9 @@ public class InstrumentationResultParser extends MultiLineReceiver {
if (mCurrentKey != null && mCurrentValue != null) {
String statusValue = mCurrentValue.toString();
if (mInInstrumentationResultKey) {
- mInstrumentationResultBundle.put(mCurrentKey, statusValue);
- if (mCurrentKey.equals(StatusKeys.SHORTMSG)) {
+ if (!KNOWN_KEYS.contains(mCurrentKey)) {
+ mInstrumentationResultBundle.put(mCurrentKey, statusValue);
+ } else if (mCurrentKey.equals(StatusKeys.SHORTMSG)) {
// test run must have failed
handleTestRunFailed(statusValue);
}
@@ -308,7 +332,7 @@ public class InstrumentationResultParser extends MultiLineReceiver {
handleTestRunFailed(statusValue);
} else if (mCurrentKey.equals(StatusKeys.STACK)) {
testInfo.mStackTrace = statusValue;
- } else {
+ } else if (!KNOWN_KEYS.contains(mCurrentKey)) {
// Not one of the recognized key/value pairs, so dump it in mTestMetrics
mTestMetrics.put(mCurrentKey, statusValue);
}
@@ -506,7 +530,7 @@ public class InstrumentationResultParser extends MultiLineReceiver {
/**
* Process a instrumentation run failure
*/
- private void handleTestRunFailed(String errorMsg) {
+ void handleTestRunFailed(String errorMsg) {
errorMsg = (errorMsg == null ? "Unknown error" : errorMsg);
Log.i(LOG_TAG, String.format("test run failed %s", errorMsg));
if (mLastTestResult != null &&
@@ -519,13 +543,19 @@ public class InstrumentationResultParser extends MultiLineReceiver {
mLastTestResult.mTestName);
for (ITestRunListener listener : mTestListeners) {
listener.testFailed(ITestRunListener.TestFailure.ERROR, testId,
- String.format("Incomplete: %s", errorMsg));
+ String.format("%s: %s", INCOMPLETE_TEST_ERR_MSG_PREFIX, errorMsg));
listener.testEnded(testId, getAndResetTestMetrics());
}
}
for (ITestRunListener listener : mTestListeners) {
+ if (!mTestStartReported) {
+ // test run wasn't started - must have crashed before it started
+ listener.testRunStarted(mTestRunName, 0);
+ }
listener.testRunFailed(errorMsg);
+ listener.testRunEnded(mTestTime, mInstrumentationResultBundle);
}
+ mTestStartReported = true;
mTestRunFailReported = true;
}
@@ -540,8 +570,8 @@ public class InstrumentationResultParser extends MultiLineReceiver {
handleTestRunFailed(NO_TEST_RESULTS_MSG);
} else if (!mTestRunFailReported && mNumTestsExpected > mNumTestsRun) {
final String message =
- String.format("Test run incomplete. Expected %d tests, received %d",
- mNumTestsExpected, mNumTestsRun);
+ String.format("%s. Expected %d tests, received %d",
+ INCOMPLETE_RUN_ERR_MSG_PREFIX, mNumTestsExpected, mNumTestsRun);
handleTestRunFailed(message);
} else {
for (ITestRunListener listener : mTestListeners) {
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 bc7b012..4c62041 100644
--- a/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
+++ b/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
@@ -233,7 +233,33 @@ public class RemoteAndroidTestRunner implements IRemoteAndroidTestRunner {
// TODO: allow run name to be configurable
mParser = new InstrumentationResultParser(mPackageName, listeners);
- mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser, mMaxTimeToOutputResponse);
+ try {
+ mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser, mMaxTimeToOutputResponse);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, String.format("IOException %s when running tests %s on %s",
+ e.toString(), getPackageName(), mRemoteDevice.getSerialNumber()));
+ // rely on parser to communicate results to listeners
+ mParser.handleTestRunFailed(e.toString());
+ throw e;
+ } catch (ShellCommandUnresponsiveException e) {
+ Log.w(LOG_TAG, String.format(
+ "ShellCommandUnresponsiveException %s when running tests %s on %s",
+ e.toString(), getPackageName(), mRemoteDevice.getSerialNumber()));
+ mParser.handleTestRunFailed(e.toString());
+ throw e;
+ } catch (TimeoutException e) {
+ Log.w(LOG_TAG, String.format(
+ "TimeoutException when running tests %s on %s", getPackageName(),
+ mRemoteDevice.getSerialNumber()));
+ mParser.handleTestRunFailed(e.toString());
+ throw e;
+ } catch (AdbCommandRejectedException e) {
+ Log.w(LOG_TAG, String.format(
+ "AdbCommandRejectedException %s when running tests %s on %s",
+ e.toString(), getPackageName(), mRemoteDevice.getSerialNumber()));
+ mParser.handleTestRunFailed(e.toString());
+ throw e;
+ }
}
/**
diff --git a/ddms/libs/ddmlib/tests/.classpath b/ddms/libs/ddmlib/tests/.classpath
index 2a5d2da..f2e9d17 100644
--- a/ddms/libs/ddmlib/tests/.classpath
+++ b/ddms/libs/ddmlib/tests/.classpath
@@ -4,5 +4,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/easymock"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/ddms/libs/ddmlib/tests/Android.mk b/ddms/libs/ddmlib/tests/Android.mk
index afec3d8..238fd60 100644
--- a/ddms/libs/ddmlib/tests/Android.mk
+++ b/ddms/libs/ddmlib/tests/Android.mk
@@ -19,9 +19,9 @@ include $(CLEAR_VARS)
# Only compile source java files in this lib.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_MODULE := ddmlibTests
+LOCAL_MODULE := ddmlib-tests
-LOCAL_JAVA_LIBRARIES := ddmlib junit
+LOCAL_JAVA_LIBRARIES := ddmlib junit easymock
include $(BUILD_HOST_JAVA_LIBRARY)
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 946e614..032b879 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
@@ -18,24 +18,29 @@ package com.android.ddmlib.testrunner;
import com.android.ddmlib.testrunner.ITestRunListener.TestFailure;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+
import junit.framework.TestCase;
+import java.util.Collections;
import java.util.Map;
-
/**
- * Tests InstrumentationResultParser.
+ * Unit tests for {@link @InstrumentationResultParser}.
*/
+@SuppressWarnings("unchecked")
public class InstrumentationResultParserTest extends TestCase {
private InstrumentationResultParser mParser;
- private VerifyingTestResult mTestResult;
+ private ITestRunListener mMockListener;
// static dummy test names to use for validation
private static final String RUN_NAME = "foo";
private static final String CLASS_NAME = "com.test.FooTest";
private static final String TEST_NAME = "testFoo";
private static final String STACK_TRACE = "java.lang.AssertionFailedException";
+ private static final TestIdentifier TEST_ID = new TestIdentifier(CLASS_NAME, TEST_NAME);
/**
* @param name - test name
@@ -50,96 +55,109 @@ public class InstrumentationResultParserTest extends TestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
- mTestResult = new VerifyingTestResult();
- mParser = new InstrumentationResultParser(RUN_NAME, mTestResult);
+ // use a strict mock to verify order of method calls
+ mMockListener = EasyMock.createStrictMock(ITestRunListener.class);
+ mParser = new InstrumentationResultParser(RUN_NAME, mMockListener);
}
/**
- * Tests that the test run started and test start events is sent on first
- * bundle received.
+ * Tests parsing empty output.
*/
- public void testTestStarted() {
- StringBuilder output = buildCommonResult();
- addStartCode(output);
+ public void testParse_empty() {
+ mMockListener.testRunStarted(RUN_NAME, 0);
+ mMockListener.testRunFailed(InstrumentationResultParser.NO_TEST_RESULTS_MSG);
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
- injectTestString(output.toString());
- assertCommonAttributes();
+ injectAndVerifyTestString("");
}
/**
- * Tests basic parsing of a single successful test execution.
+ * Tests parsing output for a successful test run with no tests.
*/
- public void testTestSuccess() {
- StringBuilder output = createSuccessTest();
+ public void testParse_noTests() {
+ StringBuilder output = new StringBuilder();
+ addLine(output, "INSTRUMENTATION_RESULT: stream=");
+ addLine(output, "Test results for InstrumentationTestRunner=");
+ addLine(output, "Time: 0.001");
+ addLine(output, "OK (0 tests)");
+ addLine(output, "INSTRUMENTATION_CODE: -1");
+
+ mMockListener.testRunStarted(RUN_NAME, 0);
+ mMockListener.testRunEnded(1, Collections.EMPTY_MAP);
- injectTestString(output.toString());
- assertCommonAttributes();
- assertEquals(1, mTestResult.mNumTestsRun);
- assertEquals(null, mTestResult.mTestStatus);
+ injectAndVerifyTestString(output.toString());
}
/**
- * Tests basic parsing of a successful test execution with metrics.
+ * Tests parsing output for a single successful test execution.
*/
- public void testTestSuccessMetrics() {
- StringBuilder output = buildCommonResult();
+ public void testParse_singleTest() {
+ StringBuilder output = createSuccessTest();
- addStatusKey(output, "randomKey", "randomValue");
- assertNotNull(mTestResult.mTestMetrics);
- assertEquals("randomValue", mTestResult.mTestMetrics.get("randomKey"));
+ mMockListener.testRunStarted(RUN_NAME, 1);
+ mMockListener.testStarted(TEST_ID);
+ mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP);
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
+
+ injectAndVerifyTestString(output.toString());
}
/**
- * Create instrumentation output for a successful single test case execution.
+ * Tests parsing output for a successful test execution with metrics.
*/
- private StringBuilder createSuccessTest() {
+ public void testParse_testMetrics() {
StringBuilder output = buildCommonResult();
+
+ addStatusKey(output, "randomKey", "randomValue");
addSuccessCode(output);
- return output;
+
+ final Capture<Map<String, String>> captureMetrics = new Capture<Map<String, String>>();
+ mMockListener.testRunStarted(RUN_NAME, 1);
+ mMockListener.testStarted(TEST_ID);
+ mMockListener.testEnded(EasyMock.eq(TEST_ID), EasyMock.capture(captureMetrics));
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
+
+ injectAndVerifyTestString(output.toString());
+
+ assertEquals("randomValue", captureMetrics.getValue().get("randomKey"));
}
/**
- * Test basic parsing of failed test case.
+ * Test parsing output for a test failure.
*/
- public void testTestFailed() {
+ public void testParse_testFailed() {
StringBuilder output = buildCommonResult();
- addStartCode(output);
- addCommonStatus(output);
addStackTrace(output);
addFailureCode(output);
- injectTestString(output.toString());
- assertCommonAttributes();
-
- assertEquals(1, mTestResult.mNumTestsRun);
- assertEquals(ITestRunListener.TestFailure.FAILURE, mTestResult.mTestStatus);
- assertEquals(STACK_TRACE, mTestResult.mTrace);
- }
+ mMockListener.testRunStarted(RUN_NAME, 1);
+ mMockListener.testStarted(TEST_ID);
+ mMockListener.testFailed(TestFailure.FAILURE, TEST_ID, STACK_TRACE);
+ mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP);
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
- /**
- * Test basic parsing and conversion of time from output.
- */
- public void testTimeParsing() {
- StringBuilder output = createSuccessTest();
- output.append("Time: 4.9");
- injectTestString(output.toString());
- assertEquals(4900, mTestResult.mTestTime);
+ injectAndVerifyTestString(output.toString());
}
/**
* Test parsing and conversion of time output that contains extra chars.
*/
- public void testTimeParsing_bracket() {
+ public void testParse_timeBracket() {
StringBuilder output = createSuccessTest();
output.append("Time: 0.001)");
- injectTestString(output.toString());
- assertEquals(1, mTestResult.mTestTime);
+
+ mMockListener.testRunStarted(RUN_NAME, 1);
+ mMockListener.testStarted(TEST_ID);
+ mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP);
+ mMockListener.testRunEnded(1, Collections.EMPTY_MAP);
+
+ injectAndVerifyTestString(output.toString());
}
/**
- * Test basic parsing of a test run failure.
+ * Test parsing output for a test run failure.
*/
- public void testRunFailed() {
+ public void testParse_runFailed() {
StringBuilder output = new StringBuilder();
final String errorMessage = "Unable to find instrumentation info";
addStatusKey(output, "Error", errorMessage);
@@ -147,16 +165,20 @@ public class InstrumentationResultParserTest extends TestCase {
output.append("INSTRUMENTATION_FAILED: com.dummy/android.test.InstrumentationTestRunner");
addLineBreak(output);
- injectTestString(output.toString());
+ mMockListener.testRunStarted(RUN_NAME, 0);
+ mMockListener.testRunFailed(errorMessage);
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
- assertEquals(errorMessage, mTestResult.mRunFailedMessage);
+ injectAndVerifyTestString(output.toString());
}
/**
- * Test parsing of a test run failure, where an instrumentation component failed to load
+ * Test parsing output for a test run failure, where an instrumentation component failed to
+ * load.
+ * <p/>
* Parsing input takes the from of INSTRUMENTATION_RESULT: fff
*/
- public void testRunFailedResult() {
+ public void testParse_failedResult() {
StringBuilder output = new StringBuilder();
final String errorMessage = "Unable to instantiate instrumentation";
output.append("INSTRUMENTATION_RESULT: shortMsg=");
@@ -165,32 +187,40 @@ public class InstrumentationResultParserTest extends TestCase {
output.append("INSTRUMENTATION_CODE: 0");
addLineBreak(output);
- injectTestString(output.toString());
+ mMockListener.testRunStarted(RUN_NAME, 0);
+ mMockListener.testRunFailed(errorMessage);
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
- assertEquals(errorMessage, mTestResult.mRunFailedMessage);
+ injectAndVerifyTestString(output.toString());
}
/**
- * Test parsing of a test run that did not complete. This can occur if device spontaneously
- * reboots, or if test method could not be found
+ * Test parsing output for a test run that did not complete.
+ * <p/>
+ * This can occur if device spontaneously reboots, or if test method could not be found.
*/
- public void testRunIncomplete() {
+ public void testParse_incomplete() {
StringBuilder output = new StringBuilder();
// add a start test sequence, but without an end test sequence
addCommonStatus(output);
addStartCode(output);
- injectTestString(output.toString());
+ mMockListener.testRunStarted(RUN_NAME, 1);
+ mMockListener.testStarted(TEST_ID);
+ mMockListener.testFailed(EasyMock.eq(TestFailure.ERROR), EasyMock.eq(TEST_ID),
+ EasyMock.startsWith(InstrumentationResultParser.INCOMPLETE_TEST_ERR_MSG_PREFIX));
+ mMockListener.testEnded(TEST_ID, Collections.EMPTY_MAP);
+ mMockListener.testRunFailed(EasyMock.startsWith(
+ InstrumentationResultParser.INCOMPLETE_RUN_ERR_MSG_PREFIX));
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
- assertTrue(mTestResult.mRunFailedMessage.startsWith("Test run incomplete."));
- // ensure test is marked as failed
- assertEquals(TestFailure.ERROR, mTestResult.mTestStatus);
+ injectAndVerifyTestString(output.toString());
}
/**
- * Test parsing of a test run that did not start due to incorrect syntax supplied to am.
+ * Test parsing output for a test run that did not start due to incorrect syntax supplied to am.
*/
- public void testRunAmFailed() {
+ public void testParse_amFailed() {
StringBuilder output = new StringBuilder();
addLine(output, "usage: am [subcommand] [options]");
addLine(output, "start an Activity: am start [-D] [-W] <INTENT>");
@@ -199,49 +229,36 @@ public class InstrumentationResultParserTest extends TestCase {
addLine(output, "start a Service: am startservice <INTENT>");
addLine(output, "Error: Bad component name: wfsdafddfasasdf");
- injectTestString(output.toString());
+ mMockListener.testRunStarted(RUN_NAME, 0);
+ mMockListener.testRunFailed(InstrumentationResultParser.NO_TEST_RESULTS_MSG);
+ mMockListener.testRunEnded(0, Collections.EMPTY_MAP);
- assertEquals(InstrumentationResultParser.NO_TEST_RESULTS_MSG,
- mTestResult.mRunFailedMessage);
+ injectAndVerifyTestString(output.toString());
}
/**
- * Test parsing of a test run that has no tests.
+ * Test parsing output for a test run that produces INSTRUMENTATION_RESULT output.
* <p/>
- * Expect run to be reported as success.
- */
- public void testRunNoResults() {
- StringBuilder output = new StringBuilder();
- addLine(output, "INSTRUMENTATION_RESULT: stream=");
- addLine(output, "Test results for InstrumentationTestRunner=");
- addLine(output, "Time: 0.001");
- addLine(output, "OK (0 tests)");
- addLine(output, "INSTRUMENTATION_CODE: -1");
-
- injectTestString(output.toString());
-
- assertEquals(0, mTestResult.mTestCount);
- assertNull(mTestResult.mRunFailedMessage);
- assertEquals(1, mTestResult.mTestTime);
- assertFalse(mTestResult.mStopped);
- }
-
- /**
- * Test parsing of a test run that produces INSTRUMENTATION_RESULT output. This mimics launch
- * performance test output.
+ * This mimics launch performance test output.
*/
- public void testRunWithInstrumentationResults() {
+ public void testParse_instrumentationResults() {
StringBuilder output = new StringBuilder();
addResultKey(output, "other_pss", "2390");
addResultKey(output, "java_allocated", "2539");
addResultKey(output, "foo", "bar");
+ addResultKey(output, "stream", "should not be captured");
addLine(output, "INSTRUMENTATION_CODE: -1");
- injectTestString(output.toString());
+ Capture<Map<String, String>> captureMetrics = new Capture<Map<String, String>>();
+ mMockListener.testRunStarted(RUN_NAME, 0);
+ mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.capture(captureMetrics));
+
+ injectAndVerifyTestString(output.toString());
- assertEquals("2390", mTestResult.mResultBundle.get("other_pss"));
- assertEquals("2539", mTestResult.mResultBundle.get("java_allocated"));
- assertEquals("bar", mTestResult.mResultBundle.get("foo"));
+ assertEquals("2390", captureMetrics.getValue().get("other_pss"));
+ assertEquals("2539", captureMetrics.getValue().get("java_allocated"));
+ assertEquals("bar", captureMetrics.getValue().get("foo"));
+ assertEquals(3, captureMetrics.getValue().size());
}
/**
@@ -258,6 +275,15 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
+ * Create instrumentation output for a successful single test case execution.
+ */
+ private StringBuilder createSuccessTest() {
+ StringBuilder output = buildCommonResult();
+ addSuccessCode(output);
+ return output;
+ }
+
+ /**
* Adds common status results to the provided output.
*/
private void addCommonStatus(StringBuilder output) {
@@ -334,87 +360,15 @@ public class InstrumentationResultParserTest extends TestCase {
}
/**
- * inject a test string into the result parser.
+ * Inject a test string into the result parser, and verify the mock listener.
*
- * @param result
+ * @param result the string to inject into parser under test.
*/
- private void injectTestString(String result) {
+ private void injectAndVerifyTestString(String result) {
+ EasyMock.replay(mMockListener);
byte[] data = result.getBytes();
mParser.addOutput(data, 0, data.length);
mParser.flush();
- }
-
- private void assertCommonAttributes() {
- assertEquals(RUN_NAME, mTestResult.mTestRunName);
- assertEquals(CLASS_NAME, mTestResult.mSuiteName);
- assertEquals(1, mTestResult.mTestCount);
- assertEquals(TEST_NAME, mTestResult.mTestName);
- }
-
- /**
- * A specialized test listener that stores a single test events.
- */
- private class VerifyingTestResult implements ITestRunListener {
-
- String mTestRunName;
- String mSuiteName;
- int mTestCount;
- int mNumTestsRun;
- String mTestName;
- long mTestTime;
- TestFailure mTestStatus;
- String mTrace;
- boolean mStopped;
- /** stores the error message provided to testRunFailed */
- String mRunFailedMessage;
- Map<String, String> mResultBundle = null;
- Map<String, String> mTestMetrics = null;
-
- VerifyingTestResult() {
- mNumTestsRun = 0;
- mTestStatus = null;
- mStopped = false;
- mRunFailedMessage = null;
- mResultBundle = null;
- }
-
- public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
- mNumTestsRun++;
- mTestMetrics = testMetrics;
- assertEquals("Unexpected class name", mSuiteName, test.getClassName());
- assertEquals("Unexpected test ended", mTestName, test.getTestName());
-
- }
-
- public void testFailed(TestFailure status, TestIdentifier test, String trace) {
- mTestStatus = status;
- mTrace = trace;
- assertEquals("Unexpected class name", mSuiteName, test.getClassName());
- assertEquals("Unexpected test ended", mTestName, test.getTestName());
- }
-
- public void testRunEnded(long elapsedTime, Map<String, String> resultBundle) {
- mTestTime = elapsedTime;
- mResultBundle = resultBundle;
- }
-
- public void testRunStarted(String runName, int testCount) {
- mTestRunName = runName;
- mTestCount = testCount;
- }
-
- public void testRunStopped(long elapsedTime) {
- mTestTime = elapsedTime;
- mStopped = true;
- }
-
- public void testStarted(TestIdentifier test) {
- mSuiteName = test.getClassName();
- mTestName = test.getTestName();
- }
-
- public void testRunFailed(String errorMessage) {
- mRunFailedMessage = errorMessage;
- }
+ EasyMock.verify(mMockListener);
}
}
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 4ee2f4d..8bde492 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
@@ -16,30 +16,24 @@
package com.android.ddmlib.testrunner;
-import com.android.ddmlib.AdbCommandRejectedException;
-import com.android.ddmlib.Client;
-import com.android.ddmlib.FileListingService;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.IShellOutputReceiver;
-import com.android.ddmlib.InstallException;
-import com.android.ddmlib.RawImage;
-import com.android.ddmlib.ShellCommandUnresponsiveException;
-import com.android.ddmlib.SyncService;
-import com.android.ddmlib.TimeoutException;
-import com.android.ddmlib.log.LogReceiver;
+
+import org.easymock.EasyMock;
import java.io.IOException;
-import java.util.Map;
+import java.util.Collections;
import junit.framework.TestCase;
/**
- * Tests RemoteAndroidTestRunner.
+ * Unit tests for {@link RemoteAndroidTestRunner}.
*/
public class RemoteAndroidTestRunnerTest extends TestCase {
private RemoteAndroidTestRunner mRunner;
- private MockDevice mMockDevice;
+ private IDevice mMockDevice;
+ private ITestRunListener mMockListener;
private static final String TEST_PACKAGE = "com.test";
private static final String TEST_RUNNER = "com.test.InstrumentationTestRunner";
@@ -49,272 +43,96 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
*/
@Override
protected void setUp() throws Exception {
- mMockDevice = new MockDevice();
+ mMockDevice = EasyMock.createMock(IDevice.class);
+ EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn("serial");
+ mMockListener = EasyMock.createNiceMock(ITestRunListener.class);
mRunner = new RemoteAndroidTestRunner(TEST_PACKAGE, TEST_RUNNER, mMockDevice);
}
/**
* Test the basic case building of the instrumentation runner command with no arguments.
- * @throws ShellCommandUnresponsiveException
- * @throws AdbCommandRejectedException
- * @throws TimeoutException
*/
- public void testRun() throws IOException, TimeoutException, AdbCommandRejectedException,
- ShellCommandUnresponsiveException {
- mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r %s/%s", TEST_PACKAGE, TEST_RUNNER),
- mMockDevice.getLastShellCommand());
+ public void testRun() throws Exception {
+ String expectedCmd = EasyMock.eq(String.format("am instrument -w -r %s/%s", TEST_PACKAGE,
+ TEST_RUNNER));
+ runAndVerify(expectedCmd);
}
/**
* Test the building of the instrumentation runner command with log set.
- * @throws ShellCommandUnresponsiveException
- * @throws AdbCommandRejectedException
- * @throws TimeoutException
*/
- public void testRunWithLog() throws IOException, TimeoutException, AdbCommandRejectedException,
- ShellCommandUnresponsiveException {
+ public void testRun_withLog() throws Exception {
mRunner.setLogOnly(true);
- mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r -e log true %s/%s", TEST_PACKAGE,
- TEST_RUNNER), mMockDevice.getLastShellCommand());
+ String expectedCmd = EasyMock.contains("-e log true");
+ runAndVerify(expectedCmd);
}
/**
* Test the building of the instrumentation runner command with method set.
- * @throws ShellCommandUnresponsiveException
- * @throws AdbCommandRejectedException
- * @throws TimeoutException
*/
- public void testRunWithMethod() throws IOException, TimeoutException,
- AdbCommandRejectedException, ShellCommandUnresponsiveException {
+ public void testRun_withMethod() throws Exception {
final String className = "FooTest";
final String testName = "fooTest";
mRunner.setMethodName(className, testName);
- mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r -e class %s#%s %s/%s", className,
- testName, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
+ String expectedCmd = EasyMock.contains(String.format("-e class %s#%s", className,
+ testName));
+ runAndVerify(expectedCmd);
}
/**
* Test the building of the instrumentation runner command with test package set.
- * @throws ShellCommandUnresponsiveException
- * @throws AdbCommandRejectedException
- * @throws TimeoutException
*/
- public void testRunWithPackage() throws IOException, TimeoutException,
- AdbCommandRejectedException, ShellCommandUnresponsiveException {
+ public void testRun_withPackage() throws Exception {
final String packageName = "foo.test";
mRunner.setTestPackageName(packageName);
- mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r -e package %s %s/%s", packageName,
- TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
+ String expectedCmd = EasyMock.contains(String.format("-e package %s", packageName));
+ runAndVerify(expectedCmd);
}
/**
* Test the building of the instrumentation runner command with extra argument added.
- * @throws ShellCommandUnresponsiveException
- * @throws AdbCommandRejectedException
- * @throws TimeoutException
*/
- public void testRunWithAddInstrumentationArg() throws IOException, TimeoutException,
- AdbCommandRejectedException, ShellCommandUnresponsiveException {
+ public void testRun_withAddInstrumentationArg() throws Exception {
final String extraArgName = "blah";
final String extraArgValue = "blahValue";
mRunner.addInstrumentationArg(extraArgName, extraArgValue);
- mRunner.run(new EmptyListener());
- assertStringsEquals(String.format("am instrument -w -r -e %s %s %s/%s", extraArgName,
- extraArgValue, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
- }
-
- /**
- * Assert two strings are equal ignoring whitespace.
- */
- private void assertStringsEquals(String str1, String str2) {
- String strippedStr1 = str1.replaceAll(" ", "");
- String strippedStr2 = str2.replaceAll(" ", "");
- assertEquals(strippedStr1, strippedStr2);
+ String expectedCmd = EasyMock.contains(String.format("-e %s %s", extraArgName,
+ extraArgValue));
+ runAndVerify(expectedCmd);
}
/**
- * A dummy device that does nothing except store the provided executed shell command for
- * later retrieval.
+ * Test run when the device throws a IOException
*/
- private static class MockDevice implements IDevice {
-
- private String mLastShellCommand;
-
- /**
- * Stores the provided command for later retrieval from getLastShellCommand.
- */
- public void executeShellCommand(String command,
- IShellOutputReceiver receiver) throws IOException {
- mLastShellCommand = command;
- }
-
- /**
- * Stores the provided command for later retrieval from getLastShellCommand.
- */
- public void executeShellCommand(String command,
- IShellOutputReceiver receiver, int timeout) throws IOException {
- mLastShellCommand = command;
- }
-
- /**
- * Get the last command provided to executeShellCommand.
- */
- public String getLastShellCommand() {
- return mLastShellCommand;
- }
-
- public void createForward(int localPort, int remotePort) {
- throw new UnsupportedOperationException();
- }
-
- public Client getClient(String applicationName) {
- throw new UnsupportedOperationException();
- }
-
- public String getClientName(int pid) {
- throw new UnsupportedOperationException();
- }
-
- public Client[] getClients() {
- throw new UnsupportedOperationException();
- }
-
- public FileListingService getFileListingService() {
- throw new UnsupportedOperationException();
- }
-
- public Map<String, String> getProperties() {
- throw new UnsupportedOperationException();
- }
-
- public String getProperty(String name) {
- throw new UnsupportedOperationException();
- }
-
- public int getPropertyCount() {
- throw new UnsupportedOperationException();
- }
-
- public String getMountPoint(String name) {
- throw new UnsupportedOperationException();
- }
-
- public RawImage getScreenshot() throws IOException {
- throw new UnsupportedOperationException();
- }
-
- public String getSerialNumber() {
- return "fakeserial";
- }
-
- public DeviceState getState() {
- throw new UnsupportedOperationException();
- }
-
- public SyncService getSyncService() {
- throw new UnsupportedOperationException();
- }
-
- public boolean hasClients() {
- throw new UnsupportedOperationException();
- }
-
- public boolean isBootLoader() {
- throw new UnsupportedOperationException();
- }
-
- public boolean isEmulator() {
- throw new UnsupportedOperationException();
- }
-
- public boolean isOffline() {
- throw new UnsupportedOperationException();
- }
-
- public boolean isOnline() {
- throw new UnsupportedOperationException();
- }
-
- public void removeForward(int localPort, int remotePort) {
- throw new UnsupportedOperationException();
- }
-
- public void runEventLogService(LogReceiver receiver) throws IOException {
- throw new UnsupportedOperationException();
- }
-
- public void runLogService(String logname, LogReceiver receiver) throws IOException {
- throw new UnsupportedOperationException();
- }
-
- public String getAvdName() {
- return "";
- }
-
- public String installPackage(String packageFilePath, boolean reinstall)
- throws InstallException {
- throw new UnsupportedOperationException();
- }
-
- public String uninstallPackage(String packageName) throws InstallException {
- throw new UnsupportedOperationException();
- }
-
- public String installRemotePackage(String remoteFilePath, boolean reinstall)
- throws InstallException {
- throw new UnsupportedOperationException();
- }
-
- public void removeRemotePackage(String remoteFilePath)
- throws InstallException {
- throw new UnsupportedOperationException();
- }
-
- public String syncPackageToDevice(String localFilePath)
- throws IOException {
- throw new UnsupportedOperationException();
- }
-
- public void reboot(String into) throws IOException {
- throw new UnsupportedOperationException();
- }
+ @SuppressWarnings("unchecked")
+ public void testRun_ioException() throws Exception {
+ mMockDevice.executeShellCommand((String)EasyMock.anyObject(), (IShellOutputReceiver)
+ EasyMock.anyObject(), EasyMock.eq(0));
+ EasyMock.expectLastCall().andThrow(new IOException());
+ // verify that the listeners run started, run failure, and run ended methods are called
+ mMockListener.testRunStarted(TEST_PACKAGE, 0);
+ mMockListener.testRunFailed((String)EasyMock.anyObject());
+ mMockListener.testRunEnded(EasyMock.anyLong(), EasyMock.eq(Collections.EMPTY_MAP));
+
+ EasyMock.replay(mMockDevice, mMockListener);
+ try {
+ mRunner.run(mMockListener);
+ fail("IOException not thrown");
+ } catch (IOException e) {
+ // expected
+ }
+ EasyMock.verify(mMockDevice, mMockListener);
}
/**
- * An empty implementation of ITestRunListener.
+ * Calls {@link RemoteAndroidTestRunner#run(ITestRunListener...)} and verifies the given
+ * <var>expectedCmd</var> pattern was received by the mock device.
*/
- private static class EmptyListener implements ITestRunListener {
-
- public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
- // ignore
- }
-
- public void testFailed(TestFailure status, TestIdentifier test, String trace) {
- // ignore
- }
-
- public void testRunEnded(long elapsedTime, Map<String, String> resultBundle) {
- // ignore
- }
-
- public void testRunFailed(String errorMessage) {
- // ignore
- }
-
- public void testRunStarted(String runName, int testCount) {
- // ignore
- }
-
- public void testRunStopped(long elapsedTime) {
- // ignore
- }
-
- public void testStarted(TestIdentifier test) {
- // ignore
- }
+ private void runAndVerify(String expectedCmd) throws Exception {
+ mMockDevice.executeShellCommand(expectedCmd, (IShellOutputReceiver)
+ EasyMock.anyObject(), EasyMock.eq(0));
+ EasyMock.replay(mMockDevice);
+ mRunner.run(mMockListener);
+ EasyMock.verify(mMockDevice);
}
}
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties
index 2670316..730e673 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/messages.properties
@@ -33,7 +33,7 @@ AndroidJUnitTab_NoRunnerError=Instrumentation runner not specified
AndroidJUnitTab_TestContainerText=Run all tests in the selected project, or package
InstrValidator_NoTestLibMsg_s=The application does not declare uses-library %1$s
InstrValidator_WrongRunnerTypeMsg_s=The instrumentation runner must be of type %1$s
-RemoteAdtTestRunner_RunCompleteMsg=Test run complete
+RemoteAdtTestRunner_RunCompleteMsg=Test run finished
RemoteAdtTestRunner_RunFailedMsg_s=Test run failed: %1$s
RemoteAdtTestRunner_RunTimeoutException=Connection with device timed out.
RemoteAdtTestRunner_RunIOException_s=Lost connection with device: %s
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
index 9888108..549611f 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/META-INF/MANIFEST.MF
@@ -10,4 +10,5 @@ Bundle-RequiredExecutionEnvironment: J2SE-1.5
Bundle-ClassPath: kxml2-2.3.0.jar,
.,
layoutlib.jar,
- groovy-all-1.7.0.jar
+ groovy-all-1.7.0.jar,
+ easymock.jar
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/build.properties b/eclipse/plugins/com.android.ide.eclipse.tests/build.properties
index 7be75ed..7c9915c 100644
--- a/eclipse/plugins/com.android.ide.eclipse.tests/build.properties
+++ b/eclipse/plugins/com.android.ide.eclipse.tests/build.properties
@@ -11,5 +11,6 @@ bin.includes = META-INF/,\
unittests/com/android/sdklib/testdata/,\
unittests/com/android/layoutlib/testdata/,\
unittests/com/android/ide/eclipse/testdata/,\
- groovy-all-1.7.0.jar
+ groovy-all-1.7.0.jar,\
+ easymock.jar