1bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/*
2bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Copyright (C) 2012 The Android Open Source Project
3bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *
4bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Licensed under the Apache License, Version 2.0 (the "License");
5bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * you may not use this file except in compliance with the License.
6bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * You may obtain a copy of the License at
7bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *
8bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *      http://www.apache.org/licenses/LICENSE-2.0
9bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *
10bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Unless required by applicable law or agreed to in writing, software
11bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * distributed under the License is distributed on an "AS IS" BASIS,
12bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * See the License for the specific language governing permissions and
14bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * limitations under the License.
15bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */
16bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpackage com.android.test.runner;
17bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
1853196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport android.app.Instrumentation;
19fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabotimport android.os.Bundle;
205e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport android.test.suitebuilder.annotation.LargeTest;
215e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport android.test.suitebuilder.annotation.MediumTest;
225e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport android.test.suitebuilder.annotation.SmallTest;
2316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabotimport android.test.suitebuilder.annotation.Suppress;
24bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport android.util.Log;
25bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
26bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport com.android.test.runner.ClassPathScanner.ChainedClassNameFilter;
27bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport com.android.test.runner.ClassPathScanner.ExcludePackageNameFilter;
28bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport com.android.test.runner.ClassPathScanner.ExternalClassNameFilter;
29bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
3053196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runner.Computer;
310e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabotimport org.junit.runner.Description;
32bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.Request;
3353196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runner.Runner;
34bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.manipulation.Filter;
3553196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.model.InitializationError;
36bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
37bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.IOException;
38bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.PrintStream;
395e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport java.lang.annotation.Annotation;
40bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Arrays;
41bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Collection;
42bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Collections;
43596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Rootimport java.util.regex.Pattern;
44bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
45bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/**
46bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Builds a {@link Request} from test classes in given apk paths, filtered on provided set of
47bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * restrictions.
48bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */
49bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpublic class TestRequestBuilder {
50bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
51bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private static final String LOG_TAG = "TestRequestBuilder";
52bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
53f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot    public static final String LARGE_SIZE = "large";
54f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot    public static final String MEDIUM_SIZE = "medium";
55f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot    public static final String SMALL_SIZE = "small";
56f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot
57bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private String[] mApkPaths;
580e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private TestLoader mTestLoader;
5916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private Filter mFilter = new AnnotationExclusionFilter(Suppress.class);
600e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private PrintStream mWriter;
613604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    private boolean mSkipExecution = false;
620e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
635e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    /**
645e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * Filter that only runs tests whose method or class has been annotated with given filter.
655e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     */
665e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    private static class AnnotationInclusionFilter extends Filter {
675e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
685e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        private final Class<? extends Annotation> mAnnotationClass;
695e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
705e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        AnnotationInclusionFilter(Class<? extends Annotation> annotation) {
715e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mAnnotationClass = annotation;
725e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
735e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
745e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        /**
755e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         * {@inheritDoc}
765e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         */
775e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        @Override
785e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        public boolean shouldRun(Description description) {
795e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            if (description.isTest()) {
805e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                return description.getAnnotation(mAnnotationClass) != null ||
815e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                        description.getTestClass().isAnnotationPresent(mAnnotationClass);
825e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            } else {
834c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // the entire test class/suite should be filtered out if all its methods are
844c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // filtered
854c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // TODO: This is not efficient since some children may end up being evaluated more
864c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // than once. This logic seems to be only necessary for JUnit3 tests. Look into
874c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // fixing in upstream
884c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                for (Description child : description.getChildren()) {
894c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                    if (shouldRun(child)) {
904c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                        return true;
914c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                    }
924c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                }
934c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // no children to run, filter this out
944c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                return false;
955e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            }
965e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
975e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
985e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        /**
995e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         * {@inheritDoc}
1005e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         */
1015e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        @Override
1025e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        public String describe() {
1035e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            return String.format("annotation %s", mAnnotationClass.getName());
1045e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
1055e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    }
1065e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
10716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
10816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Filter out tests whose method or class has been annotated with given filter.
10916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
11016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private static class AnnotationExclusionFilter extends Filter {
11116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
11216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        private final Class<? extends Annotation> mAnnotationClass;
11316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
11416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        AnnotationExclusionFilter(Class<? extends Annotation> annotation) {
11516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mAnnotationClass = annotation;
11616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
11716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
11816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        /**
11916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         * {@inheritDoc}
12016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         */
12116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        @Override
12216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        public boolean shouldRun(Description description) {
123596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            final Class<?> testClass = description.getTestClass();
124596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
125596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            /* Parameterized tests have no test classes. */
126596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            if (testClass == null) {
127596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return true;
128596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
129596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
130596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            if (testClass.isAnnotationPresent(mAnnotationClass) ||
13116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                    description.getAnnotation(mAnnotationClass) != null) {
13216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                return false;
13316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            } else {
13416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                return true;
13516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            }
13616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
13716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
13816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        /**
13916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         * {@inheritDoc}
14016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         */
14116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        @Override
14216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        public String describe() {
14316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            return String.format("not annotation %s", mAnnotationClass.getName());
14416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
14516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
14616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
1470e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public TestRequestBuilder(PrintStream writer, String... apkPaths) {
148bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        mApkPaths = apkPaths;
1490e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        mTestLoader = new TestLoader(writer);
150bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
151bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
152bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
1530e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Add a test class to be executed. All test methods in this class will be executed.
154bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     *
1550e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @param className
156bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
157bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public void addTestClass(String className) {
1580e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        mTestLoader.loadClass(className);
159bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
160bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
1610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
1620e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Adds a test method to run.
1630e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
1640e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Currently only supports one test method to be run.
1650e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
1660e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public void addTestMethod(String testClassName, String testMethodName) {
1670e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Class<?> clazz = mTestLoader.loadClass(testClassName);
1680e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (clazz != null) {
169596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            mFilter = mFilter.intersect(matchParameterizedMethod(
1700e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot                    Description.createTestDescription(clazz, testMethodName)));
1710e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        }
1720e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
1730e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
1740e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
175596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * A filter to get around the fact that parameterized tests append "[#]" at
176596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * the end of the method names. For instance, "getFoo" would become
177596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * "getFoo[0]".
178596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     */
179596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    private static Filter matchParameterizedMethod(final Description target) {
180596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root        return new Filter() {
181596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            Pattern pat = Pattern.compile(target.getMethodName() + "(\\[[0-9]+\\])?");
182596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
183596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            @Override
184596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            public boolean shouldRun(Description desc) {
185596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                if (desc.isTest()) {
186596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    return target.getClassName().equals(desc.getClassName())
187596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                            && isMatch(desc.getMethodName());
188596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                }
189596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
190596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                for (Description child : desc.getChildren()) {
191596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    if (shouldRun(child)) {
192596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                        return true;
193596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    }
194596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                }
195596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return false;
196596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
197596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
198596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            private boolean isMatch(String first) {
199596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return pat.matcher(first).matches();
200596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
201596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
202596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            @Override
203596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            public String describe() {
204596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return String.format("Method %s", target.getDisplayName());
205596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
206596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root        };
207596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    }
208596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
209596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    /**
2105e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * Run only tests with given size
2115e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * @param testSize
2125e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     */
2135e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    public void addTestSizeFilter(String testSize) {
214f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot        if (SMALL_SIZE.equals(testSize)) {
2155e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(SmallTest.class));
216f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot        } else if (MEDIUM_SIZE.equals(testSize)) {
2175e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(MediumTest.class));
218f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot        } else if (LARGE_SIZE.equals(testSize)) {
2195e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(LargeTest.class));
2205e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        } else {
2215e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            Log.e(LOG_TAG, String.format("Unrecognized test size '%s'", testSize));
2225e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
2235e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    }
2245e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
2255e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    /**
22616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Only run tests annotated with given annotation class.
22716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     *
22816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * @param annotation the full class name of annotation
22916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
23016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    public void addAnnotationInclusionFilter(String annotation) {
23116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        Class<? extends Annotation> annotationClass = loadAnnotationClass(annotation);
23216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        if (annotationClass != null) {
23316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(annotationClass));
23416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
23516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
23616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
23716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
23816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Skip tests annotated with given annotation class.
23916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     *
24016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * @param notAnnotation the full class name of annotation
24116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
24216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    public void addAnnotationExclusionFilter(String notAnnotation) {
24316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        Class<? extends Annotation> annotationClass = loadAnnotationClass(notAnnotation);
24416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        if (annotationClass != null) {
24516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mFilter = mFilter.intersect(new AnnotationExclusionFilter(annotationClass));
24616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
24716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
24816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
24916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
2503604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     * Build a request that will generate test started and test ended events, but will skip actual
2513604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     * test execution.
2523604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     */
2533604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    public void setSkipExecution(boolean b) {
2543604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot        mSkipExecution = b;
2553604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    }
2563604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot
2573604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    /**
2580e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Builds the {@link TestRequest} based on current contents of added classes and methods.
2590e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
2600e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * If no classes have been explicitly added, will scan the classpath for all tests.
2610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     *
2620e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
263fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot    public TestRequest build(Instrumentation instr, Bundle bundle) {
2640e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (mTestLoader.isEmpty()) {
2650e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            // no class restrictions have been specified. Load all classes
2660e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            loadClassesFromClassPath();
267bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
268bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
269fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        Request request = classes(instr, bundle, mSkipExecution, new Computer(),
2703604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot                mTestLoader.getLoadedClasses().toArray(new Class[0]));
2710e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return new TestRequest(mTestLoader.getLoadFailures(), request.filterWith(mFilter));
272bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
273bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
27453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    /**
27553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * Create a <code>Request</code> that, when processed, will run all the tests
27653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * in a set of classes.
27753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     *
27853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param instr the {@link Instrumentation} to inject into any tests that require it
279fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot     * @param bundle the {@link Bundle} of command line args to inject into any tests that require
280fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot     *         it
28153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param computer Helps construct Runners from classes
28253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param classes the classes containing the tests
28353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @return a <code>Request</code> that will cause all tests in the classes to be run
28453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     */
285fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot    private static Request classes(Instrumentation instr, Bundle bundle, boolean skipExecution,
2863604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot            Computer computer, Class<?>... classes) {
28753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        try {
288fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot            AndroidRunnerBuilder builder = new AndroidRunnerBuilder(true, instr, bundle,
289fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot                    skipExecution);
29053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Runner suite = computer.getSuite(builder, classes);
29153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            return Request.runner(suite);
29253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        } catch (InitializationError e) {
29353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            throw new RuntimeException(
29453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "Suite constructor, called as above, should always complete");
29553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
29653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
29753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
2980e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private void loadClassesFromClassPath() {
2990e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Collection<String> classNames = getClassNamesFromClassPath();
3000e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        for (String className : classNames) {
3010e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mTestLoader.loadIfTest(className);
3020e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        }
3030e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
3040e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
3050e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private Collection<String> getClassNamesFromClassPath() {
306bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        Log.i(LOG_TAG, String.format("Scanning classpath to find tests in apks %s",
307bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                Arrays.toString(mApkPaths)));
308bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        ClassPathScanner scanner = new ClassPathScanner(mApkPaths);
309bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        try {
31053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            // exclude inner classes, and classes from junit and this lib namespace
311bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return scanner.getClassPathEntries(new ChainedClassNameFilter(
312bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("junit"),
313bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("org.junit"),
314bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("org.hamcrest"),
31502693f872930994fffc46c600927cac3a2f82e6dBrett Chabot                    new ExcludePackageNameFilter("org.mockito"),
31602693f872930994fffc46c600927cac3a2f82e6dBrett Chabot                    new ExcludePackageNameFilter("com.android.dx"),
31702693f872930994fffc46c600927cac3a2f82e6dBrett Chabot                    new ExcludePackageNameFilter("com.google.dexmaker"),
3183604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot                    new ExternalClassNameFilter(),
3193604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot                    new ExcludePackageNameFilter("com.android.test.runner.junit3")));
320bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        } catch (IOException e) {
3210e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mWriter.println("failed to scan classes");
322bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            Log.e(LOG_TAG, "Failed to scan classes", e);
323bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
324bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        return Collections.emptyList();
325bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
3260e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
3270e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
3280e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Factory method for {@link ClassPathScanner}.
3290e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
3300e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Exposed so unit tests can mock.
3310e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
3320e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    ClassPathScanner createClassPathScanner(String... apkPaths) {
3330e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return new ClassPathScanner(apkPaths);
3340e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
33516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
33616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    @SuppressWarnings("unchecked")
33716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private Class<? extends Annotation> loadAnnotationClass(String className) {
33816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        try {
33916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Class<?> clazz = Class.forName(className);
34016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            return (Class<? extends Annotation>)clazz;
34116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        } catch (ClassNotFoundException e) {
34216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Log.e(LOG_TAG, String.format("Could not find annotation class: %s", className));
34316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        } catch (ClassCastException e) {
34416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Log.e(LOG_TAG, String.format("Class %s is not an annotation", className));
34516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
34616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        return null;
34716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
348bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot}
349