TestRequestBuilder.java revision 596c213fe3d9b8035ceb868b23cebc39dc2a3636
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;
195e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport android.test.suitebuilder.annotation.LargeTest;
205e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport android.test.suitebuilder.annotation.MediumTest;
215e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport android.test.suitebuilder.annotation.SmallTest;
2216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabotimport android.test.suitebuilder.annotation.Suppress;
23bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport android.util.Log;
24bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
25bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport com.android.test.runner.ClassPathScanner.ChainedClassNameFilter;
26bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport com.android.test.runner.ClassPathScanner.ExcludePackageNameFilter;
27bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport com.android.test.runner.ClassPathScanner.ExternalClassNameFilter;
28bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
2953196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runner.Computer;
300e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabotimport org.junit.runner.Description;
31bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.Request;
3253196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runner.Runner;
33bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.manipulation.Filter;
3453196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.model.InitializationError;
35bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
36bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.IOException;
37bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.PrintStream;
385e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport java.lang.annotation.Annotation;
39bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Arrays;
40bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Collection;
41bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Collections;
42596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Rootimport java.util.regex.Pattern;
43bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
44bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/**
45bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Builds a {@link Request} from test classes in given apk paths, filtered on provided set of
46bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * restrictions.
47bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */
48bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpublic class TestRequestBuilder {
49bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
50bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private static final String LOG_TAG = "TestRequestBuilder";
51bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
529dba8dea5781124be4e0848adba21215e622052fBrett Chabot    public static final String LARGE_SIZE = "large";
539dba8dea5781124be4e0848adba21215e622052fBrett Chabot    public static final String MEDIUM_SIZE = "medium";
549dba8dea5781124be4e0848adba21215e622052fBrett Chabot    public static final String SMALL_SIZE = "small";
559dba8dea5781124be4e0848adba21215e622052fBrett Chabot
56bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private String[] mApkPaths;
570e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private TestLoader mTestLoader;
5816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private Filter mFilter = new AnnotationExclusionFilter(Suppress.class);
590e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private PrintStream mWriter;
603604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    private boolean mSkipExecution = false;
610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
625e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    /**
635e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * Filter that only runs tests whose method or class has been annotated with given filter.
645e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     */
655e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    private static class AnnotationInclusionFilter extends Filter {
665e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
675e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        private final Class<? extends Annotation> mAnnotationClass;
685e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
695e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        AnnotationInclusionFilter(Class<? extends Annotation> annotation) {
705e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mAnnotationClass = annotation;
715e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
725e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
735e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        /**
745e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         * {@inheritDoc}
755e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         */
765e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        @Override
775e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        public boolean shouldRun(Description description) {
785e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            if (description.isTest()) {
795e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                return description.getAnnotation(mAnnotationClass) != null ||
805e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                        description.getTestClass().isAnnotationPresent(mAnnotationClass);
815e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            } else {
825e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                // don't filter out any test classes/suites, because their methods may have correct
835e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                // annotation
845e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                return true;
855e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            }
865e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
875e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
885e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        /**
895e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         * {@inheritDoc}
905e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         */
915e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        @Override
925e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        public String describe() {
935e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            return String.format("annotation %s", mAnnotationClass.getName());
945e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
955e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    }
965e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
9716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
9816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Filter out tests whose method or class has been annotated with given filter.
9916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
10016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private static class AnnotationExclusionFilter extends Filter {
10116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
10216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        private final Class<? extends Annotation> mAnnotationClass;
10316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
10416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        AnnotationExclusionFilter(Class<? extends Annotation> annotation) {
10516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mAnnotationClass = annotation;
10616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
10716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
10816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        /**
10916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         * {@inheritDoc}
11016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         */
11116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        @Override
11216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        public boolean shouldRun(Description description) {
113596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            final Class<?> testClass = description.getTestClass();
114596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
115596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            /* Parameterized tests have no test classes. */
116596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            if (testClass == null) {
117596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return true;
118596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
119596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
120596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            if (testClass.isAnnotationPresent(mAnnotationClass) ||
12116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                    description.getAnnotation(mAnnotationClass) != null) {
12216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                return false;
12316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            } else {
12416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                return true;
12516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            }
12616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
12716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
12816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        /**
12916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         * {@inheritDoc}
13016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         */
13116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        @Override
13216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        public String describe() {
13316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            return String.format("not annotation %s", mAnnotationClass.getName());
13416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
13516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
13616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
1370e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public TestRequestBuilder(PrintStream writer, String... apkPaths) {
138bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        mApkPaths = apkPaths;
1390e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        mTestLoader = new TestLoader(writer);
140bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
141bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
142bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
1430e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Add a test class to be executed. All test methods in this class will be executed.
144bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     *
1450e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @param className
146bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
147bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public void addTestClass(String className) {
1480e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        mTestLoader.loadClass(className);
149bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
150bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
1510e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
1520e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Adds a test method to run.
1530e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
1540e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Currently only supports one test method to be run.
1550e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
1560e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public void addTestMethod(String testClassName, String testMethodName) {
1570e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Class<?> clazz = mTestLoader.loadClass(testClassName);
1580e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (clazz != null) {
159596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            mFilter = mFilter.intersect(matchParameterizedMethod(
1600e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot                    Description.createTestDescription(clazz, testMethodName)));
1610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        }
1620e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
1630e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
1640e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
165596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * A filter to get around the fact that parameterized tests append "[#]" at
166596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * the end of the method names. For instance, "getFoo" would become
167596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * "getFoo[0]".
168596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     */
169596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    private static Filter matchParameterizedMethod(final Description target) {
170596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root        return new Filter() {
171596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            Pattern pat = Pattern.compile(target.getMethodName() + "(\\[[0-9]+\\])?");
172596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
173596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            @Override
174596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            public boolean shouldRun(Description desc) {
175596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                if (desc.isTest()) {
176596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    return target.getClassName().equals(desc.getClassName())
177596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                            && isMatch(desc.getMethodName());
178596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                }
179596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
180596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                for (Description child : desc.getChildren()) {
181596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    if (shouldRun(child)) {
182596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                        return true;
183596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    }
184596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                }
185596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return false;
186596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
187596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
188596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            private boolean isMatch(String first) {
189596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return pat.matcher(first).matches();
190596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
191596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
192596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            @Override
193596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            public String describe() {
194596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return String.format("Method %s", target.getDisplayName());
195596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
196596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root        };
197596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    }
198596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
199596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    /**
2005e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * Run only tests with given size
2015e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * @param testSize
2025e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     */
2035e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    public void addTestSizeFilter(String testSize) {
2049dba8dea5781124be4e0848adba21215e622052fBrett Chabot        if (SMALL_SIZE.equals(testSize)) {
2055e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(SmallTest.class));
2069dba8dea5781124be4e0848adba21215e622052fBrett Chabot        } else if (MEDIUM_SIZE.equals(testSize)) {
2075e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(MediumTest.class));
2089dba8dea5781124be4e0848adba21215e622052fBrett Chabot        } else if (LARGE_SIZE.equals(testSize)) {
2095e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(LargeTest.class));
2105e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        } else {
2115e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            Log.e(LOG_TAG, String.format("Unrecognized test size '%s'", testSize));
2125e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
2135e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    }
2145e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
2155e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    /**
21616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Only run tests annotated with given annotation class.
21716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     *
21816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * @param annotation the full class name of annotation
21916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
22016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    public void addAnnotationInclusionFilter(String annotation) {
22116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        Class<? extends Annotation> annotationClass = loadAnnotationClass(annotation);
22216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        if (annotationClass != null) {
22316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(annotationClass));
22416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
22516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
22616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
22716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
22816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Skip tests annotated with given annotation class.
22916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     *
23016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * @param notAnnotation the full class name of annotation
23116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
23216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    public void addAnnotationExclusionFilter(String notAnnotation) {
23316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        Class<? extends Annotation> annotationClass = loadAnnotationClass(notAnnotation);
23416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        if (annotationClass != null) {
23516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mFilter = mFilter.intersect(new AnnotationExclusionFilter(annotationClass));
23616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
23716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
23816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
23916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
2403604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     * Build a request that will generate test started and test ended events, but will skip actual
2413604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     * test execution.
2423604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     */
2433604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    public void setSkipExecution(boolean b) {
2443604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot        mSkipExecution = b;
2453604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    }
2463604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot
2473604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    /**
2480e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Builds the {@link TestRequest} based on current contents of added classes and methods.
2490e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
2500e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * If no classes have been explicitly added, will scan the classpath for all tests.
2510e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     *
2520e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
253cebe48ef03abf67e1957a8df4574c79191a5c482Brett Chabot    public TestRequest build(Instrumentation instr) {
2540e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (mTestLoader.isEmpty()) {
2550e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            // no class restrictions have been specified. Load all classes
2560e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            loadClassesFromClassPath();
257bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
258bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
2593604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot        Request request = classes(instr, mSkipExecution, new Computer(),
2603604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot                mTestLoader.getLoadedClasses().toArray(new Class[0]));
2610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return new TestRequest(mTestLoader.getLoadFailures(), request.filterWith(mFilter));
262bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
263bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
26453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    /**
26553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * Create a <code>Request</code> that, when processed, will run all the tests
26653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * in a set of classes.
26753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     *
26853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param instr the {@link Instrumentation} to inject into any tests that require it
26953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param computer Helps construct Runners from classes
27053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param classes the classes containing the tests
27153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @return a <code>Request</code> that will cause all tests in the classes to be run
27253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     */
2733604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    private static Request classes(Instrumentation instr, boolean skipExecution,
2743604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot            Computer computer, Class<?>... classes) {
27553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        try {
2763604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot            AndroidRunnerBuilder builder = new AndroidRunnerBuilder(true, instr, skipExecution);
27753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Runner suite = computer.getSuite(builder, classes);
27853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            return Request.runner(suite);
27953196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        } catch (InitializationError e) {
28053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            throw new RuntimeException(
28153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "Suite constructor, called as above, should always complete");
28253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
28353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
28453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
2850e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private void loadClassesFromClassPath() {
2860e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Collection<String> classNames = getClassNamesFromClassPath();
2870e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        for (String className : classNames) {
2880e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mTestLoader.loadIfTest(className);
2890e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        }
2900e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
2910e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
2920e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private Collection<String> getClassNamesFromClassPath() {
293bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        Log.i(LOG_TAG, String.format("Scanning classpath to find tests in apks %s",
294bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                Arrays.toString(mApkPaths)));
295bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        ClassPathScanner scanner = new ClassPathScanner(mApkPaths);
296bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        try {
29753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            // exclude inner classes, and classes from junit and this lib namespace
298bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return scanner.getClassPathEntries(new ChainedClassNameFilter(
299bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("junit"),
300bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("org.junit"),
301bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("org.hamcrest"),
3023604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot                    new ExternalClassNameFilter(),
3033604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot                    new ExcludePackageNameFilter("com.android.test.runner.junit3")));
304bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        } catch (IOException e) {
3050e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mWriter.println("failed to scan classes");
306bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            Log.e(LOG_TAG, "Failed to scan classes", e);
307bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
308bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        return Collections.emptyList();
309bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
3100e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
3110e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
3120e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Factory method for {@link ClassPathScanner}.
3130e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
3140e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Exposed so unit tests can mock.
3150e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
3160e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    ClassPathScanner createClassPathScanner(String... apkPaths) {
3170e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return new ClassPathScanner(apkPaths);
3180e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
31916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
32016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    @SuppressWarnings("unchecked")
32116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private Class<? extends Annotation> loadAnnotationClass(String className) {
32216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        try {
32316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Class<?> clazz = Class.forName(className);
32416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            return (Class<? extends Annotation>)clazz;
32516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        } catch (ClassNotFoundException e) {
32616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Log.e(LOG_TAG, String.format("Could not find annotation class: %s", className));
32716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        } catch (ClassCastException e) {
32816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Log.e(LOG_TAG, String.format("Class %s is not an annotation", className));
32916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
33016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        return null;
33116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
332bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot}
333