summaryrefslogtreecommitdiffstats
path: root/test-runner/src/android/test/suitebuilder/TestGrouping.java
diff options
context:
space:
mode:
Diffstat (limited to 'test-runner/src/android/test/suitebuilder/TestGrouping.java')
-rw-r--r--test-runner/src/android/test/suitebuilder/TestGrouping.java249
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")));
+ }
+ }
+}