• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 android.test.suitebuilder;
18 
19 import android.test.ClassPathPackageInfo;
20 import android.test.ClassPathPackageInfoSource;
21 import android.test.PackageInfoSources;
22 import android.util.Log;
23 import com.android.internal.util.Predicate;
24 import junit.framework.TestCase;
25 
26 import java.io.Serializable;
27 import java.lang.reflect.Constructor;
28 import java.lang.reflect.Method;
29 import java.lang.reflect.Modifier;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.Comparator;
34 import java.util.List;
35 import java.util.Set;
36 import java.util.SortedSet;
37 import java.util.TreeSet;
38 
39 /**
40  * Represents a collection of test classes present on the classpath. You can add individual classes
41  * or entire packages. By default sub-packages are included recursively, but methods are
42  * provided to allow for arbitrary inclusion or exclusion of sub-packages. Typically a
43  * {@link TestGrouping} will have only one root package, but this is not a requirement.
44  *
45  * {@hide} Not needed for 1.0 SDK.
46  */
47 public class TestGrouping {
48 
49     SortedSet<Class<? extends TestCase>> testCaseClasses;
50 
51     public static final Comparator<Class<? extends TestCase>> SORT_BY_SIMPLE_NAME
52             = new SortBySimpleName();
53 
54     public static final Comparator<Class<? extends TestCase>> SORT_BY_FULLY_QUALIFIED_NAME
55             = new SortByFullyQualifiedName();
56 
57     protected String firstIncludedPackage = null;
58     private ClassLoader classLoader;
59 
TestGrouping(Comparator<Class<? extends TestCase>> comparator)60     public TestGrouping(Comparator<Class<? extends TestCase>> comparator) {
61         testCaseClasses = new TreeSet<Class<? extends TestCase>>(comparator);
62     }
63 
64     /**
65      * @return A list of all tests in the package, including small, medium, large,
66      *         flaky, and suppressed tests. Includes sub-packages recursively.
67      */
getTests()68     public List<TestMethod> getTests() {
69         List<TestMethod> testMethods = new ArrayList<TestMethod>();
70         for (Class<? extends TestCase> testCase : testCaseClasses) {
71             for (Method testMethod : getTestMethods(testCase)) {
72                 testMethods.add(new TestMethod(testMethod, testCase));
73             }
74         }
75         return testMethods;
76     }
77 
getTestMethods(Class<? extends TestCase> testCaseClass)78     protected List<Method> getTestMethods(Class<? extends TestCase> testCaseClass) {
79         List<Method> methods = Arrays.asList(testCaseClass.getMethods());
80         return select(methods, new TestMethodPredicate());
81     }
82 
getTestCaseClasses()83     SortedSet<Class<? extends TestCase>> getTestCaseClasses() {
84         return testCaseClasses;
85     }
86 
equals(Object o)87     public boolean equals(Object o) {
88         if (this == o) {
89             return true;
90         }
91         if (o == null || getClass() != o.getClass()) {
92             return false;
93         }
94         TestGrouping other = (TestGrouping) o;
95         if (!this.testCaseClasses.equals(other.testCaseClasses)) {
96             return false;
97         }
98         return this.testCaseClasses.comparator().equals(other.testCaseClasses.comparator());
99     }
100 
hashCode()101     public int hashCode() {
102         return testCaseClasses.hashCode();
103     }
104 
105     /**
106      * Include all tests in the given packages and all their sub-packages, unless otherwise
107      * specified. Each of the given packages must contain at least one test class, either directly
108      * or in a sub-package.
109      *
110      * @param packageNames Names of packages to add.
111      * @return The {@link TestGrouping} for method chaining.
112      */
addPackagesRecursive(String... packageNames)113     public TestGrouping addPackagesRecursive(String... packageNames) {
114         for (String packageName : packageNames) {
115             List<Class<? extends TestCase>> addedClasses = testCaseClassesInPackage(packageName);
116             if (addedClasses.isEmpty()) {
117                 Log.w("TestGrouping", "Invalid Package: '" + packageName
118                         + "' could not be found or has no tests");
119             }
120             testCaseClasses.addAll(addedClasses);
121             if (firstIncludedPackage == null) {
122                 firstIncludedPackage = packageName;
123             }
124         }
125         return this;
126     }
127 
128     /**
129      * Exclude all tests in the given packages and all their sub-packages, unless otherwise
130      * specified.
131      *
132      * @param packageNames Names of packages to remove.
133      * @return The {@link TestGrouping} for method chaining.
134      */
removePackagesRecursive(String... packageNames)135     public TestGrouping removePackagesRecursive(String... packageNames) {
136         for (String packageName : packageNames) {
137             testCaseClasses.removeAll(testCaseClassesInPackage(packageName));
138         }
139         return this;
140     }
141 
142     /**
143      * @return The first package name passed to {@link #addPackagesRecursive(String[])}, or null
144      *         if that method was never called.
145      */
getFirstIncludedPackage()146     public String getFirstIncludedPackage() {
147         return firstIncludedPackage;
148     }
149 
testCaseClassesInPackage(String packageName)150     private List<Class<? extends TestCase>> testCaseClassesInPackage(String packageName) {
151         ClassPathPackageInfoSource source = PackageInfoSources.forClassPath(classLoader);
152         ClassPathPackageInfo packageInfo = source.getPackageInfo(packageName);
153 
154         return selectTestClasses(packageInfo.getTopLevelClassesRecursive());
155     }
156 
157     @SuppressWarnings("unchecked")
selectTestClasses(Set<Class<?>> allClasses)158     private List<Class<? extends TestCase>> selectTestClasses(Set<Class<?>> allClasses) {
159         List<Class<? extends TestCase>> testClasses = new ArrayList<Class<? extends TestCase>>();
160         for (Class<?> testClass : select(allClasses,
161                 new TestCasePredicate())) {
162             testClasses.add((Class<? extends TestCase>) testClass);
163         }
164         return testClasses;
165     }
166 
select(Collection<T> items, Predicate<T> predicate)167     private <T> List<T> select(Collection<T> items, Predicate<T> predicate) {
168         ArrayList<T> selectedItems = new ArrayList<T>();
169         for (T item : items) {
170             if (predicate.apply(item)) {
171                 selectedItems.add(item);
172             }
173         }
174         return selectedItems;
175     }
176 
setClassLoader(ClassLoader classLoader)177     public void setClassLoader(ClassLoader classLoader) {
178         this.classLoader = classLoader;
179     }
180 
181     /**
182      * Sort classes by their simple names (i.e. without the package prefix), using
183      * their packages to sort classes with the same name.
184      */
185     private static class SortBySimpleName
186             implements Comparator<Class<? extends TestCase>>, Serializable {
187 
compare(Class<? extends TestCase> class1, Class<? extends TestCase> class2)188         public int compare(Class<? extends TestCase> class1,
189                 Class<? extends TestCase> class2) {
190             int result = class1.getSimpleName().compareTo(class2.getSimpleName());
191             if (result != 0) {
192                 return result;
193             }
194             return class1.getName().compareTo(class2.getName());
195         }
196     }
197 
198     /**
199      * Sort classes by their fully qualified names (i.e. with the package
200      * prefix).
201      */
202     private static class SortByFullyQualifiedName
203             implements Comparator<Class<? extends TestCase>>, Serializable {
204 
compare(Class<? extends TestCase> class1, Class<? extends TestCase> class2)205         public int compare(Class<? extends TestCase> class1,
206                 Class<? extends TestCase> class2) {
207             return class1.getName().compareTo(class2.getName());
208         }
209     }
210 
211     private static class TestCasePredicate implements Predicate<Class<?>> {
212 
apply(Class aClass)213         public boolean apply(Class aClass) {
214             int modifiers = ((Class<?>) aClass).getModifiers();
215             return TestCase.class.isAssignableFrom((Class<?>) aClass)
216                     && Modifier.isPublic(modifiers)
217                     && !Modifier.isAbstract(modifiers)
218                     && hasValidConstructor((Class<?>) aClass);
219         }
220 
221         @SuppressWarnings("unchecked")
hasValidConstructor(java.lang.Class<?> aClass)222         private boolean hasValidConstructor(java.lang.Class<?> aClass) {
223             // The cast below is not necessary with the Java 5 compiler, but necessary with the Java 6 compiler,
224             // where the return type of Class.getDeclaredConstructors() was changed
225             // from Constructor<T>[] to Constructor<?>[]
226             Constructor<? extends TestCase>[] constructors
227                     = (Constructor<? extends TestCase>[]) aClass.getConstructors();
228             for (Constructor<? extends TestCase> constructor : constructors) {
229                 if (Modifier.isPublic(constructor.getModifiers())) {
230                     java.lang.Class[] parameterTypes = constructor.getParameterTypes();
231                     if (parameterTypes.length == 0 ||
232                             (parameterTypes.length == 1 && parameterTypes[0] == String.class)) {
233                         return true;
234                     }
235                 }
236             }
237             return false;
238         }
239     }
240 
241     private static class TestMethodPredicate implements Predicate<Method> {
242 
apply(Method method)243         public boolean apply(Method method) {
244             return ((method.getParameterTypes().length == 0) &&
245                     (method.getName().startsWith("test")) &&
246                     (method.getReturnType().getSimpleName().equals("void")));
247         }
248     }
249 }
250