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;
29442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabotimport com.android.test.runner.ClassPathScanner.InclusivePackageNameFilter;
30bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
3153196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runner.Computer;
320e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabotimport org.junit.runner.Description;
33bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.Request;
3453196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runner.Runner;
35bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.manipulation.Filter;
3653196f43b44ff02da07c243798168d7e5614ec34Brett Chabotimport org.junit.runners.model.InitializationError;
37bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
38bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.IOException;
39bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.PrintStream;
405e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabotimport java.lang.annotation.Annotation;
41bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Arrays;
42bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Collection;
43bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Collections;
44596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Rootimport java.util.regex.Pattern;
45bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
46bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/**
47bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Builds a {@link Request} from test classes in given apk paths, filtered on provided set of
48bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * restrictions.
49bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */
50bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpublic class TestRequestBuilder {
51bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
52bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private static final String LOG_TAG = "TestRequestBuilder";
53bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
54f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot    public static final String LARGE_SIZE = "large";
55f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot    public static final String MEDIUM_SIZE = "medium";
56f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot    public static final String SMALL_SIZE = "small";
57f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot
58bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private String[] mApkPaths;
590e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private TestLoader mTestLoader;
6016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private Filter mFilter = new AnnotationExclusionFilter(Suppress.class);
610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private PrintStream mWriter;
623604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    private boolean mSkipExecution = false;
63442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot    private String mTestPackageName = null;
640e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
655e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    /**
665e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * Filter that only runs tests whose method or class has been annotated with given filter.
675e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     */
685e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    private static class AnnotationInclusionFilter extends Filter {
695e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
705e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        private final Class<? extends Annotation> mAnnotationClass;
715e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
725e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        AnnotationInclusionFilter(Class<? extends Annotation> annotation) {
735e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mAnnotationClass = annotation;
745e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
755e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
765e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        /**
775e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         * {@inheritDoc}
785e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         */
795e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        @Override
805e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        public boolean shouldRun(Description description) {
815e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            if (description.isTest()) {
825e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                return description.getAnnotation(mAnnotationClass) != null ||
835e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot                        description.getTestClass().isAnnotationPresent(mAnnotationClass);
845e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            } else {
854c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // the entire test class/suite should be filtered out if all its methods are
864c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // filtered
874c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // TODO: This is not efficient since some children may end up being evaluated more
884c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // than once. This logic seems to be only necessary for JUnit3 tests. Look into
894c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // fixing in upstream
904c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                for (Description child : description.getChildren()) {
914c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                    if (shouldRun(child)) {
924c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                        return true;
934c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                    }
944c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                }
954c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                // no children to run, filter this out
964c88c2100e947576671cea794fca1f4fe3dc52f5Brett Chabot                return false;
975e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            }
985e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
995e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
1005e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        /**
1015e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         * {@inheritDoc}
1025e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot         */
1035e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        @Override
1045e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        public String describe() {
1055e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            return String.format("annotation %s", mAnnotationClass.getName());
1065e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
1075e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    }
1085e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
10916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
11016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Filter out tests whose method or class has been annotated with given filter.
11116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
11216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private static class AnnotationExclusionFilter extends Filter {
11316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
11416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        private final Class<? extends Annotation> mAnnotationClass;
11516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
11616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        AnnotationExclusionFilter(Class<? extends Annotation> annotation) {
11716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mAnnotationClass = annotation;
11816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
11916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
12016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        /**
12116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         * {@inheritDoc}
12216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         */
12316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        @Override
12416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        public boolean shouldRun(Description description) {
125596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            final Class<?> testClass = description.getTestClass();
126596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
127596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            /* Parameterized tests have no test classes. */
128596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            if (testClass == null) {
129596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return true;
130596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
131596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
132596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            if (testClass.isAnnotationPresent(mAnnotationClass) ||
13316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                    description.getAnnotation(mAnnotationClass) != null) {
13416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                return false;
13516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            } else {
13616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot                return true;
13716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            }
13816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
13916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
14016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        /**
14116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         * {@inheritDoc}
14216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot         */
14316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        @Override
14416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        public String describe() {
14516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            return String.format("not annotation %s", mAnnotationClass.getName());
14616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
14716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
14816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
1490e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public TestRequestBuilder(PrintStream writer, String... apkPaths) {
150bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        mApkPaths = apkPaths;
1510e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        mTestLoader = new TestLoader(writer);
152bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
153bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
154bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
1550e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Add a test class to be executed. All test methods in this class will be executed.
156bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     *
1570e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @param className
158bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
159bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public void addTestClass(String className) {
1600e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        mTestLoader.loadClass(className);
161bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
162bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
1630e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
1640e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Adds a test method to run.
1650e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
1660e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Currently only supports one test method to be run.
1670e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
1680e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public void addTestMethod(String testClassName, String testMethodName) {
1690e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Class<?> clazz = mTestLoader.loadClass(testClassName);
1700e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (clazz != null) {
171596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            mFilter = mFilter.intersect(matchParameterizedMethod(
1720e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot                    Description.createTestDescription(clazz, testMethodName)));
1730e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        }
1740e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
1750e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
1760e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
177596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * A filter to get around the fact that parameterized tests append "[#]" at
178596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * the end of the method names. For instance, "getFoo" would become
179596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     * "getFoo[0]".
180596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root     */
181596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    private static Filter matchParameterizedMethod(final Description target) {
182596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root        return new Filter() {
183596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            Pattern pat = Pattern.compile(target.getMethodName() + "(\\[[0-9]+\\])?");
184596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
185596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            @Override
186596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            public boolean shouldRun(Description desc) {
187596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                if (desc.isTest()) {
188596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    return target.getClassName().equals(desc.getClassName())
189596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                            && isMatch(desc.getMethodName());
190596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                }
191596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
192596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                for (Description child : desc.getChildren()) {
193596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    if (shouldRun(child)) {
194596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                        return true;
195596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                    }
196596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                }
197596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return false;
198596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
199596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
200596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            private boolean isMatch(String first) {
201596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return pat.matcher(first).matches();
202596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
203596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
204596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            @Override
205596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            public String describe() {
206596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root                return String.format("Method %s", target.getDisplayName());
207596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root            }
208596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root        };
209596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    }
210596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root
211596c213fe3d9b8035ceb868b23cebc39dc2a3636Kenny Root    /**
212442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot     * Run only tests within given java package
213442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot     * @param testPackage
214442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot     */
215442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot    public void addTestPackageFilter(String testPackage) {
216442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot        mTestPackageName = testPackage;
217442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot    }
218442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot
219442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot    /**
2205e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * Run only tests with given size
2215e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     * @param testSize
2225e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot     */
2235e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    public void addTestSizeFilter(String testSize) {
224f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot        if (SMALL_SIZE.equals(testSize)) {
2255e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(SmallTest.class));
226f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot        } else if (MEDIUM_SIZE.equals(testSize)) {
2275e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(MediumTest.class));
228f1b61e2846c7475a54472f6f82adaea231a35b0cBrett Chabot        } else if (LARGE_SIZE.equals(testSize)) {
2295e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(LargeTest.class));
2305e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        } else {
2315e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot            Log.e(LOG_TAG, String.format("Unrecognized test size '%s'", testSize));
2325e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot        }
2335e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    }
2345e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot
2355e6ca275201fea0fd0316fb15bc7d0248e224b2cBrett Chabot    /**
23616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Only run tests annotated with given annotation class.
23716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     *
23816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * @param annotation the full class name of annotation
23916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
24016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    public void addAnnotationInclusionFilter(String annotation) {
24116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        Class<? extends Annotation> annotationClass = loadAnnotationClass(annotation);
24216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        if (annotationClass != null) {
24316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mFilter = mFilter.intersect(new AnnotationInclusionFilter(annotationClass));
24416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
24516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
24616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
24716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
24816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * Skip tests annotated with given annotation class.
24916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     *
25016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     * @param notAnnotation the full class name of annotation
25116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot     */
25216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    public void addAnnotationExclusionFilter(String notAnnotation) {
25316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        Class<? extends Annotation> annotationClass = loadAnnotationClass(notAnnotation);
25416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        if (annotationClass != null) {
25516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            mFilter = mFilter.intersect(new AnnotationExclusionFilter(annotationClass));
25616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
25716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
25816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
25916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    /**
2603604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     * Build a request that will generate test started and test ended events, but will skip actual
2613604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     * test execution.
2623604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot     */
2633604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    public void setSkipExecution(boolean b) {
2643604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot        mSkipExecution = b;
2653604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    }
2663604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot
2673604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot    /**
2680e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Builds the {@link TestRequest} based on current contents of added classes and methods.
2690e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
2700e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * If no classes have been explicitly added, will scan the classpath for all tests.
2710e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     *
2720e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
273fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot    public TestRequest build(Instrumentation instr, Bundle bundle) {
2740e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (mTestLoader.isEmpty()) {
2750e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            // no class restrictions have been specified. Load all classes
2760e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            loadClassesFromClassPath();
277bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
278bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
279fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot        Request request = classes(instr, bundle, mSkipExecution, new Computer(),
2803604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot                mTestLoader.getLoadedClasses().toArray(new Class[0]));
2810e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return new TestRequest(mTestLoader.getLoadFailures(), request.filterWith(mFilter));
282bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
283bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
28453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    /**
28553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * Create a <code>Request</code> that, when processed, will run all the tests
28653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * in a set of classes.
28753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     *
28853196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param instr the {@link Instrumentation} to inject into any tests that require it
289fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot     * @param bundle the {@link Bundle} of command line args to inject into any tests that require
290fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot     *         it
29153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param computer Helps construct Runners from classes
29253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @param classes the classes containing the tests
29353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     * @return a <code>Request</code> that will cause all tests in the classes to be run
29453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot     */
295fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot    private static Request classes(Instrumentation instr, Bundle bundle, boolean skipExecution,
2963604db5bcd124dfd1396fb51434d3f4337690cffBrett Chabot            Computer computer, Class<?>... classes) {
29753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        try {
298fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot            AndroidRunnerBuilder builder = new AndroidRunnerBuilder(true, instr, bundle,
299fc37a0172db7197e6e0702dfa9bfdd6bed1947b1Brett Chabot                    skipExecution);
30053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            Runner suite = computer.getSuite(builder, classes);
30153196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            return Request.runner(suite);
30253196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        } catch (InitializationError e) {
30353196f43b44ff02da07c243798168d7e5614ec34Brett Chabot            throw new RuntimeException(
30453196f43b44ff02da07c243798168d7e5614ec34Brett Chabot                    "Suite constructor, called as above, should always complete");
30553196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        }
30653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot    }
30753196f43b44ff02da07c243798168d7e5614ec34Brett Chabot
3080e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private void loadClassesFromClassPath() {
3090e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Collection<String> classNames = getClassNamesFromClassPath();
3100e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        for (String className : classNames) {
3110e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mTestLoader.loadIfTest(className);
3120e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        }
3130e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
3140e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
3150e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private Collection<String> getClassNamesFromClassPath() {
316bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        Log.i(LOG_TAG, String.format("Scanning classpath to find tests in apks %s",
317bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                Arrays.toString(mApkPaths)));
318bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        ClassPathScanner scanner = new ClassPathScanner(mApkPaths);
319442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot
320442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot        ChainedClassNameFilter filter =   new ChainedClassNameFilter();
321442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot         // exclude inner classes
322442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot        filter.add(new ExternalClassNameFilter());
323442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot        if (mTestPackageName != null) {
324442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot            // request to run only a specific java package, honor that
325442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot            filter.add(new InclusivePackageNameFilter(mTestPackageName));
326442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot        } else {
327442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot            // scan all packages, but exclude junit packages
328442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot            filter.addAll(new ExcludePackageNameFilter("junit"),
329bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("org.junit"),
330bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    new ExcludePackageNameFilter("org.hamcrest"),
331442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot                    new ExcludePackageNameFilter("com.android.test.runner.junit3"));
332442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot        }
333442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot
334442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot        try {
335442abfa0f3e4ef244665ec20082c50d5cc6d4149Brett Chabot            return scanner.getClassPathEntries(filter);
336bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        } catch (IOException e) {
3370e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mWriter.println("failed to scan classes");
338bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            Log.e(LOG_TAG, "Failed to scan classes", e);
339bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
340bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        return Collections.emptyList();
341bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
3420e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
3430e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
3440e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Factory method for {@link ClassPathScanner}.
3450e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
3460e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Exposed so unit tests can mock.
3470e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
3480e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    ClassPathScanner createClassPathScanner(String... apkPaths) {
3490e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return new ClassPathScanner(apkPaths);
3500e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
35116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot
35216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    @SuppressWarnings("unchecked")
35316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    private Class<? extends Annotation> loadAnnotationClass(String className) {
35416522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        try {
35516522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Class<?> clazz = Class.forName(className);
35616522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            return (Class<? extends Annotation>)clazz;
35716522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        } catch (ClassNotFoundException e) {
35816522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Log.e(LOG_TAG, String.format("Could not find annotation class: %s", className));
35916522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        } catch (ClassCastException e) {
36016522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot            Log.e(LOG_TAG, String.format("Class %s is not an annotation", className));
36116522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        }
36216522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot        return null;
36316522ab7fbaaa627c2c51cfc1339c2248af6df1bBrett Chabot    }
364bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot}
365