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; 269e8a4c5e0d300447f53e32f0c370fbbdccebd982Brett Chabotimport java.lang.reflect.Modifier; 270e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabotimport java.util.LinkedList; 28bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.List; 29bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 30bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/** 31bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * A class for loading JUnit3 and JUnit4 test classes given a set of potential class names. 32bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */ 33bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpublic class TestLoader { 34bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 35bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot private static final String LOG_TAG = "TestLoader"; 36bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 370e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot private List<Class<?>> mLoadedClasses = new LinkedList<Class<?>>(); 380e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot private List<Failure> mLoadFailures = new LinkedList<Failure>(); 39bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 400e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot private PrintStream mWriter; 410e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot 420e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot /** 430e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * Create a {@link TestLoader}. 440e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * 450e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * @param writer a {@link PrintStream} used for reporting errors. 460e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot */ 470e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot public TestLoader(PrintStream writer) { 480e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot mWriter = writer; 490e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot } 50bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 510e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot /** 520e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * Loads the test class from the given class name. 530e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * <p/> 540e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * Will store the result internally. Successfully loaded classes can be retrieved via 550e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * {@link #getLoadedClasses()}, failures via {@link #getLoadFailures()}. 560e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * 570e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * @param className the class name to attempt to load 580e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * @return the loaded class or null. 590e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot */ 600e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot public Class<?> loadClass(String className) { 610e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot Class<?> loadedClass = doLoadClass(className); 620e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot if (loadedClass != null) { 630e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot mLoadedClasses.add(loadedClass); 64bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 650e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return loadedClass; 660e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot } 67bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 680e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot private Class<?> doLoadClass(String className) { 690e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot try { 700e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return Class.forName(className); 710e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot } catch (ClassNotFoundException e) { 720e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot String errMsg = String.format("Could not find class: %s", className); 730e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot Log.e(LOG_TAG, errMsg); 740e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot mWriter.println(errMsg); 750e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot Description description = Description.createSuiteDescription(className); 760e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot Failure failure = new Failure(description, e); 770e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot mLoadFailures.add(failure); 78bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 790e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return null; 80bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 81bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 82bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot /** 830e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * Loads the test class from the given class name. 840e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * <p/> 850e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * Similar to {@link #loadClass(String, PrintStream))}, but will ignore classes that are 860e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * not tests. 87bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * 880e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * @param className the class name to attempt to load 890e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * @return the loaded class or null. 90bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */ 910e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot public Class<?> loadIfTest(String className) { 920e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot Class<?> loadedClass = doLoadClass(className); 930e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot if (loadedClass != null && isTestClass(loadedClass)) { 940e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot mLoadedClasses.add(loadedClass); 950e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return loadedClass; 96bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 970e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return null; 980e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot } 990e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot 1000e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot /** 1010e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * @return whether this {@link TestLoader} contains any loaded classes or load failures. 1020e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot */ 1030e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot public boolean isEmpty() { 1040e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return mLoadedClasses.isEmpty() && mLoadFailures.isEmpty(); 1050e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot } 1060e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot 1070e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot /** 1080e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * Get the {@link List) of classes successfully loaded via 1090e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * {@link #loadTest(String, PrintStream)} calls. 1100e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot */ 1110e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot public List<Class<?>> getLoadedClasses() { 1120e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return mLoadedClasses; 1130e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot } 1140e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot 1150e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot /** 1160e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * Get the {@link List) of {@link Failure} that occurred during 1170e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot * {@link #loadTest(String, PrintStream)} calls. 1180e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot */ 1190e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot public List<Failure> getLoadFailures() { 1200e1d66fcd74344182e3bfca913744b1a66e7a188Brett Chabot return mLoadFailures; 121bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 122bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot 123bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot /** 124bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Determines if given class is a valid test class. 125bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * 126bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * @param loadedClass 127bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * @return <code>true</code> if loadedClass is a test 128bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */ 129bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot private boolean isTestClass(Class<?> loadedClass) { 1309e8a4c5e0d300447f53e32f0c370fbbdccebd982Brett Chabot if (Modifier.isAbstract(loadedClass.getModifiers())) { 1319e8a4c5e0d300447f53e32f0c370fbbdccebd982Brett Chabot Log.v(LOG_TAG, String.format("Skipping abstract class %s: not a test", 1329e8a4c5e0d300447f53e32f0c370fbbdccebd982Brett Chabot loadedClass.getName())); 1339e8a4c5e0d300447f53e32f0c370fbbdccebd982Brett Chabot return false; 1349e8a4c5e0d300447f53e32f0c370fbbdccebd982Brett Chabot } 135bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot // TODO: try to find upstream junit calls to replace these checks 13653196f43b44ff02da07c243798168d7e5614ec34Brett Chabot if (junit.framework.Test.class.isAssignableFrom(loadedClass)) { 137bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot return true; 138bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 139bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot // TODO: look for a 'suite' method? 140bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot if (loadedClass.isAnnotationPresent(org.junit.runner.RunWith.class)) { 141bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot return true; 142bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 143bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot for (Method testMethod : loadedClass.getMethods()) { 144bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot if (testMethod.isAnnotationPresent(org.junit.Test.class)) { 145bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot return true; 146bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 147bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 148bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot Log.v(LOG_TAG, String.format("Skipping class %s: not a test", loadedClass.getName())); 149bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot return false; 150bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot } 151bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot} 152