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