1 /* 2 * Copyright (C) 2016 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.cts.core.runner; 18 19 import android.support.test.internal.runner.ClassPathScanner; 20 import android.support.test.internal.runner.ClassPathScanner.ClassNameFilter; 21 import android.util.Log; 22 23 import com.android.cts.core.internal.runner.TestLoader; 24 25 import java.io.IOException; 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.Enumeration; 29 import java.util.List; 30 import java.util.Set; 31 32 import dalvik.system.DexFile; 33 34 /** 35 * Find tests in the current APK. 36 */ 37 public class TestClassFinder { 38 39 private static final String TAG = "TestClassFinder"; 40 private static final boolean DEBUG = false; 41 42 // Excluded test packages 43 private static final String[] DEFAULT_EXCLUDED_PACKAGES = { 44 "junit", 45 "org.junit", 46 "org.hamcrest", 47 "org.mockito",// exclude Mockito for performance and to prevent JVM related errors 48 "android.support.test.internal.runner.junit3",// always skip AndroidTestSuite 49 }; 50 getClasses(List<String> apks, ClassLoader loader)51 static Collection<Class<?>> getClasses(List<String> apks, ClassLoader loader) { 52 if (DEBUG) { 53 Log.d(TAG, "getClasses: ======================================="); 54 55 for (String apkPath : apks) { 56 Log.d(TAG, "getClasses: -------------------------------"); 57 Log.d(TAG, "getClasses: APK " + apkPath); 58 59 DexFile dexFile = null; 60 try { 61 dexFile = new DexFile(apkPath); 62 Enumeration<String> apkClassNames = dexFile.entries(); 63 while (apkClassNames.hasMoreElements()) { 64 String apkClassName = apkClassNames.nextElement(); 65 Log.d(TAG, "getClasses: DexClass element " + apkClassName); 66 } 67 } catch (IOException e) { 68 throw new AssertionError(e); 69 } finally { 70 if (dexFile != null) { 71 try { 72 dexFile.close(); 73 } catch (IOException e) { 74 throw new AssertionError(e); 75 } 76 } 77 } 78 Log.d(TAG, "getClasses: -------------------------------"); 79 } 80 } // if DEBUG 81 82 List<Class<?>> classes = new ArrayList<>(); 83 ClassPathScanner scanner = new ClassPathScanner(apks); 84 85 ClassPathScanner.ChainedClassNameFilter filter = 86 new ClassPathScanner.ChainedClassNameFilter(); 87 // exclude inner classes 88 filter.add(new ClassPathScanner.ExternalClassNameFilter()); 89 90 // exclude default classes 91 for (String defaultExcludedPackage : DEFAULT_EXCLUDED_PACKAGES) { 92 filter.add(new ExcludePackageNameFilter(defaultExcludedPackage)); 93 } 94 95 // exclude any classes that aren't a "test class" (see #loadIfTest) 96 TestLoader testLoader = new TestLoader(); 97 testLoader.setClassLoader(loader); 98 99 try { 100 Set<String> classNames = scanner.getClassPathEntries(filter); 101 for (String className : classNames) { 102 // Important: This further acts as an additional filter; 103 // classes that aren't a "test class" are never loaded. 104 Class<?> cls = testLoader.loadIfTest(className); 105 if (cls != null) { 106 classes.add(cls); 107 108 if (DEBUG) { 109 Log.d(TAG, "getClasses: Loaded " + className); 110 } 111 } else if (DEBUG) { 112 Log.d(TAG, "getClasses: Failed to load class " + className); 113 } 114 } 115 return classes; 116 } catch (IOException e) { 117 Log.e(CoreTestRunner.TAG, "Failed to scan classes", e); 118 } 119 120 121 if (DEBUG) { 122 Log.d(TAG, "getClasses: ======================================="); 123 } 124 125 return testLoader.getLoadedClasses(); 126 } 127 128 /** 129 * A {@link ClassNameFilter} that only rejects a given package names within the given namespace. 130 */ 131 public static class ExcludePackageNameFilter implements ClassNameFilter { 132 133 private final String mPkgName; 134 ExcludePackageNameFilter(String pkgName)135 ExcludePackageNameFilter(String pkgName) { 136 if (!pkgName.endsWith(".")) { 137 mPkgName = String.format("%s.", pkgName); 138 } else { 139 mPkgName = pkgName; 140 } 141 } 142 143 /** 144 * {@inheritDoc} 145 */ 146 @Override accept(String pathName)147 public boolean accept(String pathName) { 148 return !pathName.startsWith(mPkgName); 149 } 150 } 151 } 152