Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e575d3df authored by Brett Chabot's avatar Brett Chabot Committed by Android (Google) Code Review
Browse files

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

parents c747cd8b 88e03a97
Loading
Loading
Loading
Loading
+86 −1
Original line number Diff line number Diff line
@@ -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);

+40 −0
Original line number Diff line number Diff line
@@ -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 {
        }
    }
}