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 Chabot
17bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpackage com.android.test.runner;
18bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
19bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport android.util.Log;
20bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
21bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.Description;
22bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport org.junit.runner.notification.Failure;
23bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
24bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.PrintStream;
25bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.lang.reflect.Method;
260e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabotimport java.util.LinkedList;
27bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.List;
28bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
29bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/**
30bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * A class for loading JUnit3 and JUnit4 test classes given a set of potential class names.
31bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */
32bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpublic class TestLoader {
33bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
34bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private static final String LOG_TAG = "TestLoader";
35bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
360e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private  List<Class<?>> mLoadedClasses = new LinkedList<Class<?>>();
370e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private  List<Failure> mLoadFailures = new LinkedList<Failure>();
38bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
390e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private PrintStream mWriter;
400e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
410e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
420e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Create a {@link TestLoader}.
430e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     *
440e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @param writer a {@link PrintStream} used for reporting errors.
450e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
460e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public TestLoader(PrintStream writer) {
470e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        mWriter = writer;
480e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
49bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
500e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
510e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Loads the test class from the given class name.
520e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
530e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Will store the result internally. Successfully loaded classes can be retrieved via
540e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * {@link #getLoadedClasses()}, failures via {@link #getLoadFailures()}.
550e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     *
560e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @param className the class name to attempt to load
570e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @return the loaded class or null.
580e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
590e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public Class<?> loadClass(String className) {
600e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Class<?> loadedClass = doLoadClass(className);
610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (loadedClass != null) {
620e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mLoadedClasses.add(loadedClass);
63bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
640e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return loadedClass;
650e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
66bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
670e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    private Class<?> doLoadClass(String className) {
680e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        try {
690e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            return Class.forName(className);
700e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        } catch (ClassNotFoundException e) {
710e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            String errMsg = String.format("Could not find class: %s", className);
720e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            Log.e(LOG_TAG, errMsg);
730e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mWriter.println(errMsg);
740e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            Description description = Description.createSuiteDescription(className);
750e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            Failure failure = new Failure(description, e);
760e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mLoadFailures.add(failure);
77bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
780e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return null;
79bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
80bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
81bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
820e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Loads the test class from the given class name.
830e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * <p/>
840e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Similar to {@link #loadClass(String, PrintStream))}, but will ignore classes that are
850e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * not tests.
86bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     *
870e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @param className the class name to attempt to load
880e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @return the loaded class or null.
89bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
900e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public Class<?> loadIfTest(String className) {
910e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        Class<?> loadedClass = doLoadClass(className);
920e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        if (loadedClass != null && isTestClass(loadedClass)) {
930e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            mLoadedClasses.add(loadedClass);
940e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot            return loadedClass;
95bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
960e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return null;
970e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
980e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
990e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
1000e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * @return whether this {@link TestLoader} contains any loaded classes or load failures.
1010e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
1020e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public boolean isEmpty() {
1030e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return mLoadedClasses.isEmpty() && mLoadFailures.isEmpty();
1040e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
1050e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
1060e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
1070e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Get the {@link List) of classes successfully loaded via
1080e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * {@link #loadTest(String, PrintStream)} calls.
1090e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
1100e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public List<Class<?>> getLoadedClasses() {
1110e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return mLoadedClasses;
1120e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    }
1130e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot
1140e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    /**
1150e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * Get the {@link List) of {@link Failure} that occurred during
1160e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     * {@link #loadTest(String, PrintStream)} calls.
1170e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot     */
1180e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot    public List<Failure> getLoadFailures() {
1190e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot        return mLoadFailures;
120bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
121bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
122bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
123bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * Determines if given class is a valid test class.
124bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     *
125bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * @param loadedClass
126bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * @return <code>true</code> if loadedClass is a test
127bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
128bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private boolean isTestClass(Class<?> loadedClass) {
129bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        // TODO: try to find upstream junit calls to replace these checks
13053196f43b44ff02da07c243798168d7e5614ec34Brett Chabot        if (junit.framework.Test.class.isAssignableFrom(loadedClass)) {
131bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return true;
132bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
133bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        // TODO: look for a 'suite' method?
134bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        if (loadedClass.isAnnotationPresent(org.junit.runner.RunWith.class)) {
135bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return true;
136bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
137bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        for (Method testMethod : loadedClass.getMethods()) {
138bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            if (testMethod.isAnnotationPresent(org.junit.Test.class)) {
139bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                return true;
140bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            }
141bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
142bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        Log.v(LOG_TAG, String.format("Skipping class %s: not a test", loadedClass.getName()));
143bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        return false;
144bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
145bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot}
146