diff options
Diffstat (limited to 'test-runner/src/android/test/suitebuilder/TestGrouping.java')
-rw-r--r-- | test-runner/src/android/test/suitebuilder/TestGrouping.java | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/test-runner/src/android/test/suitebuilder/TestGrouping.java b/test-runner/src/android/test/suitebuilder/TestGrouping.java new file mode 100644 index 0000000..df6da70 --- /dev/null +++ b/test-runner/src/android/test/suitebuilder/TestGrouping.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.test.suitebuilder; + +import android.test.ClassPathPackageInfo; +import android.test.ClassPathPackageInfoSource; +import android.test.PackageInfoSources; +import android.util.Log; +import com.android.internal.util.Predicate; +import junit.framework.TestCase; + +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Represents a collection of test classes present on the classpath. You can add individual classes + * or entire packages. By default sub-packages are included recursively, but methods are + * provided to allow for arbitrary inclusion or exclusion of sub-packages. Typically a + * {@link TestGrouping} will have only one root package, but this is not a requirement. + * + * {@hide} Not needed for 1.0 SDK. + */ +public class TestGrouping { + + SortedSet<Class<? extends TestCase>> testCaseClasses; + + public static final Comparator<Class<? extends TestCase>> SORT_BY_SIMPLE_NAME + = new SortBySimpleName(); + + public static final Comparator<Class<? extends TestCase>> SORT_BY_FULLY_QUALIFIED_NAME + = new SortByFullyQualifiedName(); + + protected String firstIncludedPackage = null; + private ClassLoader classLoader; + + public TestGrouping(Comparator<Class<? extends TestCase>> comparator) { + testCaseClasses = new TreeSet<Class<? extends TestCase>>(comparator); + } + + /** + * @return A list of all tests in the package, including small, medium, large, + * flaky, and suppressed tests. Includes sub-packages recursively. + */ + public List<TestMethod> getTests() { + List<TestMethod> testMethods = new ArrayList<TestMethod>(); + for (Class<? extends TestCase> testCase : testCaseClasses) { + for (Method testMethod : getTestMethods(testCase)) { + testMethods.add(new TestMethod(testMethod, testCase)); + } + } + return testMethods; + } + + protected List<Method> getTestMethods(Class<? extends TestCase> testCaseClass) { + List<Method> methods = Arrays.asList(testCaseClass.getMethods()); + return select(methods, new TestMethodPredicate()); + } + + SortedSet<Class<? extends TestCase>> getTestCaseClasses() { + return testCaseClasses; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TestGrouping other = (TestGrouping) o; + if (!this.testCaseClasses.equals(other.testCaseClasses)) { + return false; + } + return this.testCaseClasses.comparator().equals(other.testCaseClasses.comparator()); + } + + public int hashCode() { + return testCaseClasses.hashCode(); + } + + /** + * Include all tests in the given packages and all their sub-packages, unless otherwise + * specified. Each of the given packages must contain at least one test class, either directly + * or in a sub-package. + * + * @param packageNames Names of packages to add. + * @return The {@link TestGrouping} for method chaining. + */ + public TestGrouping addPackagesRecursive(String... packageNames) { + for (String packageName : packageNames) { + List<Class<? extends TestCase>> addedClasses = testCaseClassesInPackage(packageName); + if (addedClasses.isEmpty()) { + Log.w("TestGrouping", "Invalid Package: '" + packageName + + "' could not be found or has no tests"); + } + testCaseClasses.addAll(addedClasses); + if (firstIncludedPackage == null) { + firstIncludedPackage = packageName; + } + } + return this; + } + + /** + * Exclude all tests in the given packages and all their sub-packages, unless otherwise + * specified. + * + * @param packageNames Names of packages to remove. + * @return The {@link TestGrouping} for method chaining. + */ + public TestGrouping removePackagesRecursive(String... packageNames) { + for (String packageName : packageNames) { + testCaseClasses.removeAll(testCaseClassesInPackage(packageName)); + } + return this; + } + + /** + * @return The first package name passed to {@link #addPackagesRecursive(String[])}, or null + * if that method was never called. + */ + public String getFirstIncludedPackage() { + return firstIncludedPackage; + } + + private List<Class<? extends TestCase>> testCaseClassesInPackage(String packageName) { + ClassPathPackageInfoSource source = PackageInfoSources.forClassPath(classLoader); + ClassPathPackageInfo packageInfo = source.getPackageInfo(packageName); + + return selectTestClasses(packageInfo.getTopLevelClassesRecursive()); + } + + @SuppressWarnings("unchecked") + private List<Class<? extends TestCase>> selectTestClasses(Set<Class<?>> allClasses) { + List<Class<? extends TestCase>> testClasses = new ArrayList<Class<? extends TestCase>>(); + for (Class<?> testClass : select(allClasses, + new TestCasePredicate())) { + testClasses.add((Class<? extends TestCase>) testClass); + } + return testClasses; + } + + private <T> List<T> select(Collection<T> items, Predicate<T> predicate) { + ArrayList<T> selectedItems = new ArrayList<T>(); + for (T item : items) { + if (predicate.apply(item)) { + selectedItems.add(item); + } + } + return selectedItems; + } + + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + /** + * Sort classes by their simple names (i.e. without the package prefix), using + * their packages to sort classes with the same name. + */ + private static class SortBySimpleName + implements Comparator<Class<? extends TestCase>>, Serializable { + + public int compare(Class<? extends TestCase> class1, + Class<? extends TestCase> class2) { + int result = class1.getSimpleName().compareTo(class2.getSimpleName()); + if (result != 0) { + return result; + } + return class1.getName().compareTo(class2.getName()); + } + } + + /** + * Sort classes by their fully qualified names (i.e. with the package + * prefix). + */ + private static class SortByFullyQualifiedName + implements Comparator<Class<? extends TestCase>>, Serializable { + + public int compare(Class<? extends TestCase> class1, + Class<? extends TestCase> class2) { + return class1.getName().compareTo(class2.getName()); + } + } + + private static class TestCasePredicate implements Predicate<Class<?>> { + + public boolean apply(Class aClass) { + int modifiers = ((Class<?>) aClass).getModifiers(); + return TestCase.class.isAssignableFrom((Class<?>) aClass) + && Modifier.isPublic(modifiers) + && !Modifier.isAbstract(modifiers) + && hasValidConstructor((Class<?>) aClass); + } + + @SuppressWarnings("unchecked") + private boolean hasValidConstructor(java.lang.Class<?> aClass) { + // The cast below is not necessary with the Java 5 compiler, but necessary with the Java 6 compiler, + // where the return type of Class.getDeclaredConstructors() was changed + // from Constructor<T>[] to Constructor<?>[] + Constructor<? extends TestCase>[] constructors + = (Constructor<? extends TestCase>[]) aClass.getConstructors(); + for (Constructor<? extends TestCase> constructor : constructors) { + if (Modifier.isPublic(constructor.getModifiers())) { + java.lang.Class[] parameterTypes = constructor.getParameterTypes(); + if (parameterTypes.length == 0 || + (parameterTypes.length == 1 && parameterTypes[0] == String.class)) { + return true; + } + } + } + return false; + } + } + + private static class TestMethodPredicate implements Predicate<Method> { + + public boolean apply(Method method) { + return ((method.getParameterTypes().length == 0) && + (method.getName().startsWith("test")) && + (method.getReturnType().getSimpleName().equals("void"))); + } + } +} |