Home | History | Annotate | Download | only in runner
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.test.runner;
     18 
     19 import android.util.Log;
     20 
     21 import org.junit.runner.Description;
     22 import org.junit.runner.notification.Failure;
     23 
     24 import java.io.PrintStream;
     25 import java.lang.reflect.Method;
     26 import java.lang.reflect.Modifier;
     27 import java.util.LinkedList;
     28 import java.util.List;
     29 
     30 /**
     31  * A class for loading JUnit3 and JUnit4 test classes given a set of potential class names.
     32  */
     33 public class TestLoader {
     34 
     35     private static final String LOG_TAG = "TestLoader";
     36 
     37     private  List<Class<?>> mLoadedClasses = new LinkedList<Class<?>>();
     38     private  List<Failure> mLoadFailures = new LinkedList<Failure>();
     39 
     40     private PrintStream mWriter;
     41 
     42     /**
     43      * Create a {@link TestLoader}.
     44      *
     45      * @param writer a {@link PrintStream} used for reporting errors.
     46      */
     47     public TestLoader(PrintStream writer) {
     48         mWriter = writer;
     49     }
     50 
     51     /**
     52      * Loads the test class from the given class name.
     53      * <p/>
     54      * Will store the result internally. Successfully loaded classes can be retrieved via
     55      * {@link #getLoadedClasses()}, failures via {@link #getLoadFailures()}.
     56      *
     57      * @param className the class name to attempt to load
     58      * @return the loaded class or null.
     59      */
     60     public Class<?> loadClass(String className) {
     61         Class<?> loadedClass = doLoadClass(className);
     62         if (loadedClass != null) {
     63             mLoadedClasses.add(loadedClass);
     64         }
     65         return loadedClass;
     66     }
     67 
     68     private Class<?> doLoadClass(String className) {
     69         try {
     70             return Class.forName(className);
     71         } catch (ClassNotFoundException e) {
     72             String errMsg = String.format("Could not find class: %s", className);
     73             Log.e(LOG_TAG, errMsg);
     74             mWriter.println(errMsg);
     75             Description description = Description.createSuiteDescription(className);
     76             Failure failure = new Failure(description, e);
     77             mLoadFailures.add(failure);
     78         }
     79         return null;
     80     }
     81 
     82     /**
     83      * Loads the test class from the given class name.
     84      * <p/>
     85      * Similar to {@link #loadClass(String, PrintStream))}, but will ignore classes that are
     86      * not tests.
     87      *
     88      * @param className the class name to attempt to load
     89      * @return the loaded class or null.
     90      */
     91     public Class<?> loadIfTest(String className) {
     92         Class<?> loadedClass = doLoadClass(className);
     93         if (loadedClass != null && isTestClass(loadedClass)) {
     94             mLoadedClasses.add(loadedClass);
     95             return loadedClass;
     96         }
     97         return null;
     98     }
     99 
    100     /**
    101      * @return whether this {@link TestLoader} contains any loaded classes or load failures.
    102      */
    103     public boolean isEmpty() {
    104         return mLoadedClasses.isEmpty() && mLoadFailures.isEmpty();
    105     }
    106 
    107     /**
    108      * Get the {@link List) of classes successfully loaded via
    109      * {@link #loadTest(String, PrintStream)} calls.
    110      */
    111     public List<Class<?>> getLoadedClasses() {
    112         return mLoadedClasses;
    113     }
    114 
    115     /**
    116      * Get the {@link List) of {@link Failure} that occurred during
    117      * {@link #loadTest(String, PrintStream)} calls.
    118      */
    119     public List<Failure> getLoadFailures() {
    120         return mLoadFailures;
    121     }
    122 
    123     /**
    124      * Determines if given class is a valid test class.
    125      *
    126      * @param loadedClass
    127      * @return <code>true</code> if loadedClass is a test
    128      */
    129     private boolean isTestClass(Class<?> loadedClass) {
    130         if (Modifier.isAbstract(loadedClass.getModifiers())) {
    131             Log.v(LOG_TAG, String.format("Skipping abstract class %s: not a test",
    132                     loadedClass.getName()));
    133             return false;
    134         }
    135         // TODO: try to find upstream junit calls to replace these checks
    136         if (junit.framework.Test.class.isAssignableFrom(loadedClass)) {
    137             return true;
    138         }
    139         // TODO: look for a 'suite' method?
    140         if (loadedClass.isAnnotationPresent(org.junit.runner.RunWith.class)) {
    141             return true;
    142         }
    143         for (Method testMethod : loadedClass.getMethods()) {
    144             if (testMethod.isAnnotationPresent(org.junit.Test.class)) {
    145                 return true;
    146             }
    147         }
    148         Log.v(LOG_TAG, String.format("Skipping class %s: not a test", loadedClass.getName()));
    149         return false;
    150     }
    151 }
    152