am ff3ae2fb: am 0aac9989: Move the uiautomator test libraries to frameworks/testing to solve the unbundle configure issue.
# Via Android Git Automerger (1) and Xia Wang (1)
* commit 'ff3ae2fbec12b4f1b7947f999d3a20e62c53a5b3':
Move the uiautomator test libraries to frameworks/testing to solve the unbundle configure issue.
diff --git a/androidtestlib/src/com/android/test/runner/AndroidJUnitRunner.java b/androidtestlib/src/com/android/test/runner/AndroidJUnitRunner.java
index ddab2b6..a932523 100644
--- a/androidtestlib/src/com/android/test/runner/AndroidJUnitRunner.java
+++ b/androidtestlib/src/com/android/test/runner/AndroidJUnitRunner.java
@@ -24,15 +24,21 @@
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
+import com.android.test.runner.listener.CoverageListener;
+import com.android.test.runner.listener.DelayInjector;
+import com.android.test.runner.listener.InstrumentationResultPrinter;
+import com.android.test.runner.listener.InstrumentationRunListener;
+import com.android.test.runner.listener.SuiteAssignmentPrinter;
+
import org.junit.internal.TextListener;
-import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
-import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
/**
* An {@link Instrumentation} that runs JUnit3 and JUnit4 tests against
@@ -104,82 +110,33 @@
* test execution. Useful for quickly obtaining info on the tests to be executed by an
* instrumentation command.
* <p/>
+ * <b>To generate EMMA code coverage:</b>
+ * -e coverage true
+ * Note: this requires an emma instrumented build. By default, the code coverage results file
+ * will be saved in a /data/<app>/coverage.ec file, unless overridden by coverageFile flag (see
+ * below)
+ * <p/>
+ * <b> To specify EMMA code coverage results file path:</b>
+ * -e coverageFile /sdcard/myFile.ec
+ * <p/>
*/
public class AndroidJUnitRunner extends Instrumentation {
+ // constants for supported instrumentation arguments
public static final String ARGUMENT_TEST_CLASS = "class";
-
private static final String ARGUMENT_TEST_SIZE = "size";
private static final String ARGUMENT_LOG_ONLY = "log";
private static final String ARGUMENT_ANNOTATION = "annotation";
private static final String ARGUMENT_NOT_ANNOTATION = "notAnnotation";
+ private static final String ARGUMENT_DELAY_MSEC = "delay_msec";
+ private static final String ARGUMENT_COVERAGE = "coverage";
+ private static final String ARGUMENT_COVERAGE_PATH = "coverageFile";
+ private static final String ARGUMENT_SUITE_ASSIGNMENT = "suiteAssignment";
+ private static final String ARGUMENT_DEBUG = "debug";
+ // TODO: consider supporting 'count' from InstrumentationTestRunner
- /**
- * The following keys are used in the status bundle to provide structured reports to
- * an IInstrumentationWatcher.
- */
+ private static final String LOG_TAG = "AndroidJUnitRunner";
- /**
- * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER},
- * identifies InstrumentationTestRunner as the source of the report. This is sent with all
- * status messages.
- */
- public static final String REPORT_VALUE_ID = "InstrumentationTestRunner";
- /**
- * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
- * identifies the total number of tests that are being run. This is sent with all status
- * messages.
- */
- public static final String REPORT_KEY_NUM_TOTAL = "numtests";
- /**
- * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
- * identifies the sequence number of the current test. This is sent with any status message
- * describing a specific test being started or completed.
- */
- public static final String REPORT_KEY_NUM_CURRENT = "current";
- /**
- * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
- * identifies the name of the current test class. This is sent with any status message
- * describing a specific test being started or completed.
- */
- public static final String REPORT_KEY_NAME_CLASS = "class";
- /**
- * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
- * identifies the name of the current test. This is sent with any status message
- * describing a specific test being started or completed.
- */
- public static final String REPORT_KEY_NAME_TEST = "test";
-
- /**
- * The test is starting.
- */
- public static final int REPORT_VALUE_RESULT_START = 1;
- /**
- * The test completed successfully.
- */
- public static final int REPORT_VALUE_RESULT_OK = 0;
- /**
- * The test completed with an error.
- */
- public static final int REPORT_VALUE_RESULT_ERROR = -1;
- /**
- * The test completed with a failure.
- */
- public static final int REPORT_VALUE_RESULT_FAILURE = -2;
- /**
- * The test was ignored.
- */
- public static final int REPORT_VALUE_RESULT_IGNORED = -3;
- /**
- * If included in the status bundle sent to an IInstrumentationWatcher, this key
- * identifies a stack trace describing an error or failure. This is sent with any status
- * message describing a specific test being completed.
- */
- public static final String REPORT_KEY_STACK = "stack";
-
- private static final String LOG_TAG = "InstrumentationTestRunner";
-
- private final Bundle mResults = new Bundle();
private Bundle mArguments;
@Override
@@ -200,8 +157,17 @@
return mArguments;
}
- private boolean getBooleanArgument(Bundle arguments, String tag) {
- String tagString = arguments.getString(tag);
+ /**
+ * Set the arguments.
+ *
+ * @VisibleForTesting
+ */
+ void setArguments(Bundle args) {
+ mArguments = args;
+ }
+
+ private boolean getBooleanArgument(String tag) {
+ String tagString = getArguments().getString(tag);
return tagString != null && Boolean.parseBoolean(tagString);
}
@@ -218,17 +184,17 @@
public void onStart() {
prepareLooper();
- if (getBooleanArgument(getArguments(), "debug")) {
+ if (getBooleanArgument(ARGUMENT_DEBUG)) {
Debug.waitForDebugger();
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream writer = new PrintStream(byteArrayOutputStream);
+ List<RunListener> listeners = new ArrayList<RunListener>();
+
try {
JUnitCore testRunner = new JUnitCore();
- testRunner.addListener(new TextListener(writer));
- WatcherResultPrinter detailedResultPrinter = new WatcherResultPrinter();
- testRunner.addListener(detailedResultPrinter);
+ addListeners(listeners, testRunner, writer);
TestRequest testRequest = buildRequest(getArguments(), writer);
Result result = testRunner.run(testRequest.getRequest());
@@ -243,15 +209,65 @@
t.printStackTrace(writer);
} finally {
+ Bundle results = new Bundle();
+ reportRunEnded(listeners, writer, results);
writer.close();
- mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
+ results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
String.format("\n%s",
byteArrayOutputStream.toString()));
- finish(Activity.RESULT_OK, mResults);
+ finish(Activity.RESULT_OK, results);
}
}
+ private void addListeners(List<RunListener> listeners, JUnitCore testRunner,
+ PrintStream writer) {
+ if (getBooleanArgument(ARGUMENT_SUITE_ASSIGNMENT)) {
+ addListener(listeners, testRunner, new SuiteAssignmentPrinter(writer));
+ } else {
+ addListener(listeners, testRunner, new TextListener(writer));
+ addListener(listeners, testRunner, new InstrumentationResultPrinter(this));
+ addDelayListener(listeners, testRunner);
+ addCoverageListener(listeners, testRunner);
+ }
+ }
+
+ private void addListener(List<RunListener> list, JUnitCore testRunner, RunListener listener) {
+ list.add(listener);
+ testRunner.addListener(listener);
+ }
+
+ private void addCoverageListener(List<RunListener> list, JUnitCore testRunner) {
+ if (getBooleanArgument(ARGUMENT_COVERAGE)) {
+ String coverageFilePath = getArguments().getString(ARGUMENT_COVERAGE_PATH);
+ addListener(list, testRunner, new CoverageListener(this, coverageFilePath));
+ }
+ }
+
+ /**
+ * Sets up listener to inject {@link #ARGUMENT_DELAY_MSEC}, if specified.
+ * @param testRunner
+ */
+ private void addDelayListener(List<RunListener> list, JUnitCore testRunner) {
+ try {
+ Object delay = getArguments().get(ARGUMENT_DELAY_MSEC); // Accept either string or int
+ if (delay != null) {
+ int delayMsec = Integer.parseInt(delay.toString());
+ addListener(list, testRunner, new DelayInjector(delayMsec));
+ }
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Invalid delay_msec parameter", e);
+ }
+ }
+
+ private void reportRunEnded(List<RunListener> listeners, PrintStream writer, Bundle results) {
+ for (RunListener listener : listeners) {
+ if (listener instanceof InstrumentationRunListener) {
+ ((InstrumentationRunListener)listener).instrumentationRunFinished(writer, results);
+ }
+ }
+ }
+
/**
* Builds a {@link TestRequest} based on given input arguments.
* <p/>
@@ -286,8 +302,7 @@
builder.addAnnotationExclusionFilter(notAnnotation);
}
- boolean logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
- if (logOnly) {
+ if (getBooleanArgument(ARGUMENT_LOG_ONLY)) {
builder.setSkipExecution(true);
}
return builder.build(this);
@@ -320,91 +335,4 @@
testRequestBuilder.addTestClass(testClassName);
}
}
-
- /**
- * This class sends status reports back to the IInstrumentationWatcher
- */
- private class WatcherResultPrinter extends RunListener {
- private final Bundle mResultTemplate;
- Bundle mTestResult;
- int mTestNum = 0;
- int mTestResultCode = 0;
- String mTestClass = null;
-
- public WatcherResultPrinter() {
- mResultTemplate = new Bundle();
- }
-
- @Override
- public void testRunStarted(Description description) throws Exception {
- mResultTemplate.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
- mResultTemplate.putInt(REPORT_KEY_NUM_TOTAL, description.testCount());
- }
-
- @Override
- public void testRunFinished(Result result) throws Exception {
- // TODO: implement this
- }
-
- /**
- * send a status for the start of a each test, so long tests can be seen
- * as "running"
- */
- @Override
- public void testStarted(Description description) throws Exception {
- String testClass = description.getClassName();
- String testName = description.getMethodName();
- mTestResult = new Bundle(mResultTemplate);
- mTestResult.putString(REPORT_KEY_NAME_CLASS, testClass);
- mTestResult.putString(REPORT_KEY_NAME_TEST, testName);
- mTestResult.putInt(REPORT_KEY_NUM_CURRENT, ++mTestNum);
- // pretty printing
- if (testClass != null && !testClass.equals(mTestClass)) {
- mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
- String.format("\n%s:", testClass));
- mTestClass = testClass;
- } else {
- mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "");
- }
-
- sendStatus(REPORT_VALUE_RESULT_START, mTestResult);
- mTestResultCode = 0;
- }
-
- @Override
- public void testFinished(Description description) throws Exception {
- if (mTestResultCode == 0) {
- mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, ".");
- }
- sendStatus(mTestResultCode, mTestResult);
- }
-
- @Override
- public void testFailure(Failure failure) throws Exception {
- mTestResultCode = REPORT_VALUE_RESULT_ERROR;
- reportFailure(failure);
- }
-
-
- @Override
- public void testAssumptionFailure(Failure failure) {
- mTestResultCode = REPORT_VALUE_RESULT_FAILURE;
- reportFailure(failure);
- }
-
- private void reportFailure(Failure failure) {
- mTestResult.putString(REPORT_KEY_STACK, failure.getTrace());
- // pretty printing
- mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
- String.format("\nError in %s:\n%s",
- failure.getDescription().getDisplayName(), failure.getTrace()));
- }
-
- @Override
- public void testIgnored(Description description) throws Exception {
- testStarted(description);
- mTestResultCode = REPORT_VALUE_RESULT_IGNORED;
- testFinished(description);
- }
- }
}
diff --git a/androidtestlib/src/com/android/test/runner/TestLoader.java b/androidtestlib/src/com/android/test/runner/TestLoader.java
index d5ad737..97c1083 100644
--- a/androidtestlib/src/com/android/test/runner/TestLoader.java
+++ b/androidtestlib/src/com/android/test/runner/TestLoader.java
@@ -23,6 +23,7 @@
import java.io.PrintStream;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
@@ -126,6 +127,11 @@
* @return <code>true</code> if loadedClass is a test
*/
private boolean isTestClass(Class<?> loadedClass) {
+ if (Modifier.isAbstract(loadedClass.getModifiers())) {
+ Log.v(LOG_TAG, String.format("Skipping abstract class %s: not a test",
+ loadedClass.getName()));
+ return false;
+ }
// TODO: try to find upstream junit calls to replace these checks
if (junit.framework.Test.class.isAssignableFrom(loadedClass)) {
return true;
diff --git a/androidtestlib/src/com/android/test/runner/TestRequestBuilder.java b/androidtestlib/src/com/android/test/runner/TestRequestBuilder.java
index 250a9fc..a15b70e 100644
--- a/androidtestlib/src/com/android/test/runner/TestRequestBuilder.java
+++ b/androidtestlib/src/com/android/test/runner/TestRequestBuilder.java
@@ -48,6 +48,10 @@
private static final String LOG_TAG = "TestRequestBuilder";
+ public static final String LARGE_SIZE = "large";
+ public static final String MEDIUM_SIZE = "medium";
+ public static final String SMALL_SIZE = "small";
+
private String[] mApkPaths;
private TestLoader mTestLoader;
private Filter mFilter = new AnnotationExclusionFilter(Suppress.class);
@@ -154,11 +158,11 @@
* @param testSize
*/
public void addTestSizeFilter(String testSize) {
- if ("small".equals(testSize)) {
+ if (SMALL_SIZE.equals(testSize)) {
mFilter = mFilter.intersect(new AnnotationInclusionFilter(SmallTest.class));
- } else if ("medium".equals(testSize)) {
+ } else if (MEDIUM_SIZE.equals(testSize)) {
mFilter = mFilter.intersect(new AnnotationInclusionFilter(MediumTest.class));
- } else if ("large".equals(testSize)) {
+ } else if (LARGE_SIZE.equals(testSize)) {
mFilter = mFilter.intersect(new AnnotationInclusionFilter(LargeTest.class));
} else {
Log.e(LOG_TAG, String.format("Unrecognized test size '%s'", testSize));
diff --git a/androidtestlib/src/com/android/test/runner/listener/CoverageListener.java b/androidtestlib/src/com/android/test/runner/listener/CoverageListener.java
new file mode 100644
index 0000000..4b36ea3
--- /dev/null
+++ b/androidtestlib/src/com/android/test/runner/listener/CoverageListener.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.runner.listener;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * A test {@link RunListener} that generates EMMA code coverage.
+ */
+public class CoverageListener extends InstrumentationRunListener {
+
+ private String mCoverageFilePath;
+
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * identifies the path to the generated code coverage file.
+ */
+ private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath";
+ // Default file name for code coverage
+ private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec";
+
+ private static final String LOG_TAG = null;
+
+ /**
+ * Creates a {@link CoverageListener).
+ *
+ * @param instr the {@link Instrumentation} that the test is running under
+ * @param customCoverageFilePath an optional user specified path for the coverage file
+ * If null, file will be generated in test app's file directory.
+ */
+ public CoverageListener(Instrumentation instr, String customCoverageFilePath) {
+ super(instr);
+ mCoverageFilePath = customCoverageFilePath;
+ if (mCoverageFilePath == null) {
+ mCoverageFilePath = instr.getTargetContext().getFilesDir().getAbsolutePath() +
+ File.separator + DEFAULT_COVERAGE_FILE_NAME;
+ }
+ }
+
+ @Override
+ public void instrumentationRunFinished(PrintStream writer, Bundle results) {
+ generateCoverageReport(writer, results);
+ }
+
+ private void generateCoverageReport(PrintStream writer, Bundle results) {
+ // use reflection to call emma dump coverage method, to avoid
+ // always statically compiling against emma jar
+ java.io.File coverageFile = new java.io.File(mCoverageFilePath);
+ try {
+ Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
+ Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
+ coverageFile.getClass(), boolean.class, boolean.class);
+
+ dumpCoverageMethod.invoke(null, coverageFile, false, false);
+
+ // output path to generated coverage file so it can be parsed by a test harness if
+ // needed
+ results.putString(REPORT_KEY_COVERAGE_PATH, mCoverageFilePath);
+ // also output a more user friendly msg
+ writer.format("\nGenerated code coverage data to %s",mCoverageFilePath);
+ } catch (ClassNotFoundException e) {
+ reportEmmaError(writer, "Is emma jar on classpath?", e);
+ } catch (SecurityException e) {
+ reportEmmaError(writer, e);
+ } catch (NoSuchMethodException e) {
+ reportEmmaError(writer, e);
+ } catch (IllegalArgumentException e) {
+ reportEmmaError(writer, e);
+ } catch (IllegalAccessException e) {
+ reportEmmaError(writer, e);
+ } catch (InvocationTargetException e) {
+ reportEmmaError(writer, e);
+ }
+ }
+
+ private void reportEmmaError(PrintStream writer, Exception e) {
+ reportEmmaError(writer, "", e);
+ }
+
+ private void reportEmmaError(PrintStream writer, String hint, Exception e) {
+ String msg = "Failed to generate emma coverage. " + hint;
+ Log.e(LOG_TAG, msg, e);
+ writer.format("\nError: %s", msg);
+ }
+}
diff --git a/androidtestlib/src/com/android/test/runner/listener/DelayInjector.java b/androidtestlib/src/com/android/test/runner/listener/DelayInjector.java
new file mode 100644
index 0000000..b092029
--- /dev/null
+++ b/androidtestlib/src/com/android/test/runner/listener/DelayInjector.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.runner.listener;
+
+import android.util.Log;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunListener;
+
+/**
+ * A {@link RunListener} that injects a given delay between tests.
+ */
+public class DelayInjector extends RunListener {
+
+ private final int mDelayMsec;
+
+ /**
+ * @param delayMsec
+ */
+ public DelayInjector(int delayMsec) {
+ mDelayMsec = delayMsec;
+ }
+
+ @Override
+ public void testRunStarted(Description description) throws Exception {
+ // delay before first test
+ delay();
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ // delay after every test
+ delay();
+ }
+
+ private void delay() {
+ try {
+ Thread.sleep(mDelayMsec);
+ } catch (InterruptedException e) {
+ Log.e("DelayInjector", "interrupted", e);
+ }
+ }
+}
diff --git a/androidtestlib/src/com/android/test/runner/listener/InstrumentationResultPrinter.java b/androidtestlib/src/com/android/test/runner/listener/InstrumentationResultPrinter.java
new file mode 100644
index 0000000..df392e2
--- /dev/null
+++ b/androidtestlib/src/com/android/test/runner/listener/InstrumentationResultPrinter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.runner.listener;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+
+/**
+ * A {@link RunListener} that sends detailed pass/fail results back as instrumentation status
+ * bundles. This output appears when running the instrumentation in '-r' or raw mode.
+ */
+public class InstrumentationResultPrinter extends InstrumentationRunListener {
+
+ /**
+ * This value, if stored with key {@link android.app.Instrumentation#REPORT_KEY_IDENTIFIER},
+ * identifies AndroidJUnitRunner as the source of the report. This is sent with all
+ * status messages.
+ */
+ public static final String REPORT_VALUE_ID = "AndroidJUnitRunner";
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * identifies the total number of tests that are being run. This is sent with all status
+ * messages.
+ */
+ public static final String REPORT_KEY_NUM_TOTAL = "numtests";
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * identifies the sequence number of the current test. This is sent with any status message
+ * describing a specific test being started or completed.
+ */
+ public static final String REPORT_KEY_NUM_CURRENT = "current";
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * identifies the name of the current test class. This is sent with any status message
+ * describing a specific test being started or completed.
+ */
+ public static final String REPORT_KEY_NAME_CLASS = "class";
+ /**
+ * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
+ * identifies the name of the current test. This is sent with any status message
+ * describing a specific test being started or completed.
+ */
+ public static final String REPORT_KEY_NAME_TEST = "test";
+
+ /**
+ * The test is starting.
+ */
+ public static final int REPORT_VALUE_RESULT_START = 1;
+ /**
+ * The test completed successfully.
+ */
+ public static final int REPORT_VALUE_RESULT_OK = 0;
+ /**
+ * The test completed with an error.
+ */
+ public static final int REPORT_VALUE_RESULT_ERROR = -1;
+ /**
+ * The test completed with a failure.
+ */
+ public static final int REPORT_VALUE_RESULT_FAILURE = -2;
+ /**
+ * The test was ignored.
+ */
+ public static final int REPORT_VALUE_RESULT_IGNORED = -3;
+ /**
+ * If included in the status bundle sent to an IInstrumentationWatcher, this key
+ * identifies a stack trace describing an error or failure. This is sent with any status
+ * message describing a specific test being completed.
+ */
+ public static final String REPORT_KEY_STACK = "stack";
+
+ private final Bundle mResultTemplate;
+ Bundle mTestResult;
+ int mTestNum = 0;
+ int mTestResultCode = 0;
+ String mTestClass = null;
+
+ public InstrumentationResultPrinter(Instrumentation i) {
+ super(i);
+ mResultTemplate = new Bundle();
+ }
+
+ @Override
+ public void testRunStarted(Description description) throws Exception {
+ mResultTemplate.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID);
+ mResultTemplate.putInt(REPORT_KEY_NUM_TOTAL, description.testCount());
+ }
+
+ @Override
+ public void testRunFinished(Result result) throws Exception {
+ }
+
+ /**
+ * send a status for the start of a each test, so long tests can be seen
+ * as "running"
+ */
+ @Override
+ public void testStarted(Description description) throws Exception {
+ String testClass = description.getClassName();
+ String testName = description.getMethodName();
+ mTestResult = new Bundle(mResultTemplate);
+ mTestResult.putString(REPORT_KEY_NAME_CLASS, testClass);
+ mTestResult.putString(REPORT_KEY_NAME_TEST, testName);
+ mTestResult.putInt(REPORT_KEY_NUM_CURRENT, ++mTestNum);
+ // pretty printing
+ if (testClass != null && !testClass.equals(mTestClass)) {
+ mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
+ String.format("\n%s:", testClass));
+ mTestClass = testClass;
+ } else {
+ mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, "");
+ }
+
+ sendStatus(REPORT_VALUE_RESULT_START, mTestResult);
+ mTestResultCode = 0;
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ if (mTestResultCode == 0) {
+ mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT, ".");
+ }
+ sendStatus(mTestResultCode, mTestResult);
+ }
+
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ mTestResultCode = REPORT_VALUE_RESULT_ERROR;
+ reportFailure(failure);
+ }
+
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ mTestResultCode = REPORT_VALUE_RESULT_FAILURE;
+ reportFailure(failure);
+ }
+
+ private void reportFailure(Failure failure) {
+ mTestResult.putString(REPORT_KEY_STACK, failure.getTrace());
+ // pretty printing
+ mTestResult.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
+ String.format("\nError in %s:\n%s",
+ failure.getDescription().getDisplayName(), failure.getTrace()));
+ }
+
+ @Override
+ public void testIgnored(Description description) throws Exception {
+ testStarted(description);
+ mTestResultCode = REPORT_VALUE_RESULT_IGNORED;
+ testFinished(description);
+ }
+}
diff --git a/androidtestlib/src/com/android/test/runner/listener/InstrumentationRunListener.java b/androidtestlib/src/com/android/test/runner/listener/InstrumentationRunListener.java
new file mode 100644
index 0000000..e9370af
--- /dev/null
+++ b/androidtestlib/src/com/android/test/runner/listener/InstrumentationRunListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test.runner.listener;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+import org.junit.runner.notification.RunListener;
+
+import java.io.PrintStream;
+
+/**
+ * A {@link RunListener} that has access to a {@link Instrumentation}. This is useful for
+ * test result listeners that want to dump data back to the instrumentation results.
+ */
+public abstract class InstrumentationRunListener extends RunListener {
+
+ private final Instrumentation mInstr;
+
+ public InstrumentationRunListener(Instrumentation instr) {
+ mInstr = instr;
+ }
+
+ public Instrumentation getInstrumentation() {
+ return mInstr;
+ }
+
+ /**
+ * Convenience method for {@link #getInstrumentation()#sendStatus()}
+ */
+ public void sendStatus(int code, Bundle bundle) {
+ getInstrumentation().sendStatus(code, bundle);
+ }
+
+ /**
+ * Optional callback subclasses can implement. Will be called when instrumentation run
+ * completes.
+ *
+ * @param streamResult the {@link PrintStream} to instrumentation out.
+ * @param resultBundle the instrumentation result bundle. Can be used to inject key-value pairs
+ * into the instrumentation output when run in -r/raw mode
+ */
+ public void instrumentationRunFinished(PrintStream streamResult, Bundle resultBundle) {
+ }
+}
diff --git a/androidtestlib/src/com/android/test/runner/listener/SuiteAssignmentPrinter.java b/androidtestlib/src/com/android/test/runner/listener/SuiteAssignmentPrinter.java
new file mode 100644
index 0000000..ac4aa2c
--- /dev/null
+++ b/androidtestlib/src/com/android/test/runner/listener/SuiteAssignmentPrinter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.runner.listener;
+
+import com.android.test.runner.TestRequestBuilder;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+import java.io.PrintStream;
+
+/**
+ * This class measures the elapsed run time of each test, and used it to report back to the user
+ * which suite ({@link SmallSuite}, {@link MediumSuite}, {@link LargeSuite}) the test should belong
+ * to.
+ */
+public class SuiteAssignmentPrinter extends RunListener {
+ /**
+ * This constant defines the maximum allowed runtime (in ms) for a test included in the "small"
+ * suite. It is used to make an educated guess at what suite an unlabeled test belongs to.
+ */
+ private static final float SMALL_SUITE_MAX_RUNTIME = 100;
+
+ /**
+ * This constant defines the maximum allowed runtime (in ms) for a test included in the "medium"
+ * suite. It is used to make an educated guess at what suite an unlabeled test belongs to.
+ */
+ private static final float MEDIUM_SUITE_MAX_RUNTIME = 1000;
+
+ private final PrintStream mWriter;
+
+ public SuiteAssignmentPrinter(PrintStream writer) {
+ mWriter = writer;
+ }
+
+ private long mStartTime;
+ private boolean mTimingValid;
+
+ @Override
+ public void testStarted(Description description) throws Exception {
+ mTimingValid = true;
+ mStartTime = System.currentTimeMillis();
+ }
+
+ @Override
+ public void testFinished(Description description) throws Exception {
+ long runTime;
+ String assignmentSuite;
+ long endTime = System.currentTimeMillis();
+
+ if (!mTimingValid || mStartTime < 0) {
+ assignmentSuite = "NA";
+ runTime = -1;
+ } else {
+ runTime = endTime - mStartTime;
+ if (runTime < SMALL_SUITE_MAX_RUNTIME) {
+ assignmentSuite = TestRequestBuilder.SMALL_SIZE;
+ } else if (runTime < MEDIUM_SUITE_MAX_RUNTIME) {
+ assignmentSuite = TestRequestBuilder.MEDIUM_SIZE;
+ } else {
+ assignmentSuite = TestRequestBuilder.LARGE_SIZE;
+ }
+ }
+ // Clear mStartTime so that we can verify that it gets set next time.
+ mStartTime = -1;
+
+ mWriter.printf("%s#%s\n" + "in %s suite\n" + "runTime: %d ms\n",
+ description.getClassName(), description.getMethodName(), assignmentSuite,
+ runTime);
+ }
+
+ @Override
+ public void testFailure(Failure failure) throws Exception {
+ mTimingValid = false;
+ }
+
+ @Override
+ public void testAssumptionFailure(Failure failure) {
+ mTimingValid = false;
+ }
+
+ @Override
+ public void testIgnored(Description description) throws Exception {
+ mTimingValid = false;
+ }
+}
diff --git a/androidtestlib/tests/AndroidManifest.xml b/androidtestlib/tests/AndroidManifest.xml
index 36a84af..c8c181e 100644
--- a/androidtestlib/tests/AndroidManifest.xml
+++ b/androidtestlib/tests/AndroidManifest.xml
@@ -23,6 +23,7 @@
<instrumentation android:name="com.android.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.testlib.tests"
android:label="Unit Tests for testlib."/>
+
<uses-sdk android:minSdkVersion="8"/>
</manifest>
diff --git a/androidtestlib/tests/src/com/android/test/MyAndroidTestCase.java b/androidtestlib/tests/src/com/android/test/MyAndroidTestCase.java
index 1e328ac..f48337e 100644
--- a/androidtestlib/tests/src/com/android/test/MyAndroidTestCase.java
+++ b/androidtestlib/tests/src/com/android/test/MyAndroidTestCase.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.test.AndroidTestCase;
+import android.util.Log;
/**
* Placeholder test to verify {@link Context} gets injected to {@link AndroidTestCase}.
@@ -24,4 +25,8 @@
public class MyAndroidTestCase extends AndroidTestCase {
// rely on testCaseSetupProperly to test for context
+
+ public MyAndroidTestCase() {
+ Log.i("MyAndroidTestCase", "I'm created");
+ }
}
diff --git a/androidtestlib/tests/src/com/android/test/runner/AndroidJUnitRunnerTest.java b/androidtestlib/tests/src/com/android/test/runner/AndroidJUnitRunnerTest.java
index 010e23f..6a24a47 100644
--- a/androidtestlib/tests/src/com/android/test/runner/AndroidJUnitRunnerTest.java
+++ b/androidtestlib/tests/src/com/android/test/runner/AndroidJUnitRunnerTest.java
@@ -55,6 +55,7 @@
return mMockContext;
}
};
+ mAndroidJUnitRunner.setArguments(new Bundle());
mStubStream = new PrintStream(new ByteArrayOutputStream());
LittleMock.initMocks(this);
}
diff --git a/androidtestlib/tests/src/com/android/test/runner/TestLoaderTest.java b/androidtestlib/tests/src/com/android/test/runner/TestLoaderTest.java
index 5ffff5c..34cadc1 100644
--- a/androidtestlib/tests/src/com/android/test/runner/TestLoaderTest.java
+++ b/androidtestlib/tests/src/com/android/test/runner/TestLoaderTest.java
@@ -34,6 +34,9 @@
public static class JUnit3Test extends TestCase {
}
+ public static abstract class AbstractTest extends TestCase {
+ }
+
public static class JUnit4Test {
@Test
public void thisIsATest() {
@@ -93,4 +96,11 @@
Assert.assertEquals(0, mLoader.getLoadFailures().size());
Assert.assertTrue(mLoader.getLoadedClasses().contains(clazz));
}
+
+ @Test
+ public void testLoadTests_abstract() {
+ Assert.assertNull(mLoader.loadIfTest(AbstractTest.class.getName()));
+ Assert.assertEquals(0, mLoader.getLoadedClasses().size());
+ Assert.assertEquals(0, mLoader.getLoadFailures().size());
+ }
}