1 /* 2 * Copyright (C) 2023 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 package com.android.hoststubgen.hosthelper; 17 18 import com.google.common.reflect.ClassPath; 19 import com.google.common.reflect.ClassPath.ClassInfo; 20 21 import junit.framework.JUnit4TestAdapter; 22 import junit.framework.TestSuite; 23 24 import java.util.regex.Pattern; 25 26 /** 27 * A very simple Junit {@link TestSuite} builder that finds all classes that look like test classes. 28 * 29 * We use it to run ravenwood test jars from the command line. 30 */ 31 public class HostTestSuite { 32 private static final String CLASS_NAME_REGEX_ENV = "HOST_TEST_CLASS_NAME_REGEX"; 33 34 /** 35 * Called by junit, and return all test-looking classes as a suite. 36 */ suite()37 public static TestSuite suite() { 38 TestSuite suite = new TestSuite(); 39 40 final Pattern classNamePattern; 41 final var filterRegex = System.getenv(CLASS_NAME_REGEX_ENV); 42 if (filterRegex == null) { 43 classNamePattern = Pattern.compile(""); 44 } else { 45 classNamePattern = Pattern.compile(filterRegex); 46 } 47 try { 48 // We use guava to list all classes. 49 ClassPath cp = ClassPath.from(HostTestSuite.class.getClassLoader()); 50 51 for (var classInfo : cp.getAllClasses()) { 52 Class<?> clazz = asTestClass(classInfo); 53 if (clazz != null) { 54 if (classNamePattern.matcher(clazz.getSimpleName()).find()) { 55 System.out.println("Test class found " + clazz.getName()); 56 } else { 57 System.out.println("Skipping test class (for $" 58 + CLASS_NAME_REGEX_ENV + "): " + clazz.getName()); 59 } 60 suite.addTest(new JUnit4TestAdapter(clazz)); 61 } 62 } 63 } catch (Exception e) { 64 throw new RuntimeException(e); 65 } 66 return suite; 67 } 68 69 /** 70 * Decide whether a class looks like a test class or not, and if so, return it as a Class 71 * instance. 72 */ asTestClass(ClassInfo classInfo)73 private static Class<?> asTestClass(ClassInfo classInfo) { 74 try { 75 final Class<?> clazz = classInfo.load(); 76 77 // Does it extend junit.framework.TestCase? 78 if (junit.framework.TestCase.class.isAssignableFrom(clazz)) { 79 // Ignore classes in JUnit itself, or the android(x) test lib. 80 if (classInfo.getName().startsWith("junit.") 81 || classInfo.getName().startsWith("org.junit.") 82 || classInfo.getName().startsWith("android.test.") 83 || classInfo.getName().startsWith("androidx.test.")) { 84 return null; // Ignore junit classes. 85 } 86 return clazz; 87 } 88 // Does it have any "@Test" method? 89 for (var method : clazz.getMethods()) { 90 for (var an : method.getAnnotations()) { 91 if (an.annotationType() == org.junit.Test.class) { 92 return clazz; 93 } 94 } 95 } 96 return null; 97 } catch (java.lang.NoClassDefFoundError e) { 98 // Ignore unloadable classes. 99 return null; 100 } 101 } 102 } 103