Merge "Add ability to run tests restricted to given annotation."

This commit is contained in:
Brett Chabot
2010-02-23 10:46:42 -08:00
committed by Android (Google) Code Review
2 changed files with 126 additions and 1 deletions

View File

@@ -19,6 +19,7 @@ package android.test;
import static android.test.suitebuilder.TestPredicates.REJECT_PERFORMANCE;
import com.android.internal.util.Predicate;
import com.android.internal.util.Predicates;
import android.app.Activity;
import android.app.Instrumentation;
@@ -31,11 +32,13 @@ import android.os.PerformanceCollector.PerformanceResultsWriter;
import android.test.suitebuilder.TestMethod;
import android.test.suitebuilder.TestPredicates;
import android.test.suitebuilder.TestSuiteBuilder;
import android.test.suitebuilder.annotation.HasAnnotation;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -93,6 +96,18 @@ import junit.textui.ResultPrinter;
* -e size large
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
* <b>Filter test run to tests with given annotation:</b> adb shell am instrument -w
* -e annotation com.android.foo.MyAnnotation
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
* If used with other options, the resulting test run will contain the union of the two options.
* e.g. "-e size large -e annotation com.android.foo.MyAnnotation" will run only tests with both
* the {@link LargeTest} and "com.android.foo.MyAnnotation" annotations.
* <p/>
* <b>Filter test run to tests <i>without</i> given annotation:</b> adb shell am instrument -w
* -e notAnnotation com.android.foo.MyAnnotation
* com.android.foo/android.test.InstrumentationTestRunner
* <p/>
* <b>Running a single testcase:</b> adb shell am instrument -w
* -e class com.android.foo.FooTest
* com.android.foo/android.test.InstrumentationTestRunner
@@ -161,6 +176,10 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
private static final String LARGE_SUITE = "large";
private static final String ARGUMENT_LOG_ONLY = "log";
/** @hide */
static final String ARGUMENT_ANNOTATION = "annotation";
/** @hide */
static final String ARGUMENT_NOT_ANNOTATION = "notAnnotation";
/**
* This constant defines the maximum allowed runtime (in ms) for a test included in the "small"
@@ -274,6 +293,8 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
ClassPathPackageInfoSource.setApkPaths(apkPaths);
Predicate<TestMethod> testSizePredicate = null;
Predicate<TestMethod> testAnnotationPredicate = null;
Predicate<TestMethod> testNotAnnotationPredicate = null;
boolean includePerformance = false;
String testClassesArg = null;
boolean logOnly = false;
@@ -287,6 +308,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
mPackageOfTests = arguments.getString(ARGUMENT_TEST_PACKAGE);
testSizePredicate = getSizePredicateFromArg(
arguments.getString(ARGUMENT_TEST_SIZE_PREDICATE));
testAnnotationPredicate = getAnnotationPredicate(
arguments.getString(ARGUMENT_ANNOTATION));
testNotAnnotationPredicate = getNotAnnotationPredicate(
arguments.getString(ARGUMENT_NOT_ANNOTATION));
includePerformance = getBooleanArgument(arguments, ARGUMENT_INCLUDE_PERF);
logOnly = getBooleanArgument(arguments, ARGUMENT_LOG_ONLY);
mCoverage = getBooleanArgument(arguments, "coverage");
@@ -306,6 +332,12 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
if (testSizePredicate != null) {
testSuiteBuilder.addRequirements(testSizePredicate);
}
if (testAnnotationPredicate != null) {
testSuiteBuilder.addRequirements(testAnnotationPredicate);
}
if (testNotAnnotationPredicate != null) {
testSuiteBuilder.addRequirements(testNotAnnotationPredicate);
}
if (!includePerformance) {
testSuiteBuilder.addRequirements(REJECT_PERFORMANCE);
}
@@ -406,6 +438,59 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
}
}
/**
* Returns the test predicate object, corresponding to the annotation class value provided via
* the {@link ARGUMENT_ANNOTATION} argument.
*
* @return the predicate or <code>null</code>
*/
private Predicate<TestMethod> getAnnotationPredicate(String annotationClassName) {
Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName);
if (annotationClass != null) {
return new HasAnnotation(annotationClass);
}
return null;
}
/**
* Returns the negative test predicate object, corresponding to the annotation class value
* provided via the {@link ARGUMENT_NOT_ANNOTATION} argument.
*
* @return the predicate or <code>null</code>
*/
private Predicate<TestMethod> getNotAnnotationPredicate(String annotationClassName) {
Class<? extends Annotation> annotationClass = getAnnotationClass(annotationClassName);
if (annotationClass != null) {
return Predicates.not(new HasAnnotation(annotationClass));
}
return null;
}
/**
* Helper method to return the annotation class with specified name
*
* @param annotationClassName the fully qualified name of the class
* @return the annotation class or <code>null</code>
*/
private Class<? extends Annotation> getAnnotationClass(String annotationClassName) {
if (annotationClassName == null) {
return null;
}
try {
Class<?> annotationClass = Class.forName(annotationClassName);
if (annotationClass.isAnnotation()) {
return (Class<? extends Annotation>)annotationClass;
} else {
Log.e(LOG_TAG, String.format("Provided annotation value %s is not an Annotation",
annotationClassName));
}
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, String.format("Could not find class for specified annotation %s",
annotationClassName));
}
return null;
}
@Override
public void onStart() {
Looper.prepare();
@@ -471,7 +556,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
String coverageFilePath = getCoverageFilePath();
java.io.File coverageFile = new java.io.File(coverageFilePath);
try {
Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
coverageFile.getClass(), boolean.class, boolean.class);

View File

@@ -109,6 +109,33 @@ public class InstrumentationTestRunnerTest extends TestCase {
assertTrue(mStubAndroidTestRunner.isRun());
}
/**
* Test that the -e {@link InstrumentationTestRunner.ARGUMENT_ANNOTATION} parameter properly
* selects tests.
*/
public void testAnnotationParameter() throws Exception {
String expectedTestClassName = AnnotationTest.class.getName();
Bundle args = new Bundle();
args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName);
args.putString(InstrumentationTestRunner.ARGUMENT_ANNOTATION, FlakyTest.class.getName());
mInstrumentationTestRunner.onCreate(args);
assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testAnnotated");
}
/**
* Test that the -e {@link InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION} parameter
* properly excludes tests.
*/
public void testNotAnnotationParameter() throws Exception {
String expectedTestClassName = AnnotationTest.class.getName();
Bundle args = new Bundle();
args.putString(InstrumentationTestRunner.ARGUMENT_TEST_CLASS, expectedTestClassName);
args.putString(InstrumentationTestRunner.ARGUMENT_NOT_ANNOTATION,
FlakyTest.class.getName());
mInstrumentationTestRunner.onCreate(args);
assertTestRunnerCalledWithExpectedParameters(expectedTestClassName, "testNotAnnotated");
}
private void assertContentsInOrder(List<TestDescriptor> actual, TestDescriptor... source) {
TestDescriptor[] clonedSource = source.clone();
assertEquals("Unexpected number of items.", clonedSource.length, actual.size());
@@ -269,4 +296,17 @@ public class InstrumentationTestRunnerTest extends TestCase {
}
}
/**
* Annotated test used for validation.
*/
public static class AnnotationTest extends TestCase {
public void testNotAnnotated() throws Exception {
}
@FlakyTest
public void testAnnotated() throws Exception {
}
}
}