1 /* 2 * Copyright (C) 2024 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.ctsprofiles; 18 19 import java.util.ArrayList; 20 import java.util.HashMap; 21 import java.util.HashSet; 22 import java.util.List; 23 import java.util.Map; 24 import java.util.Set; 25 26 /** Representation of a class included in the CTS package. */ 27 public class ClassProfile { 28 29 public final AnnotationManagement annotationManagement = new AnnotationManagement(); 30 31 private final String mModule; 32 33 private final String mPackage; 34 35 private final String mClass; 36 37 private int mClassType = 0; 38 39 private ClassProfile mSuperClass = null; 40 41 // A list of interfaces implemented by this class. 42 private final List<ClassProfile> mInterfaces = new ArrayList<>(); 43 44 // A map of methods defined in this class with the method signature as the key. 45 private final Map<String, MethodProfile> mMethods = new HashMap<>(); 46 47 // A map of test methods defined in this class with the method signature as the key. 48 private Map<String, MethodProfile> mTestMethods = null; 49 50 // TODO(slotus): Add known patterns. 51 private static final Set<String> JUNIT4_ANNOTATION_PATTERNS = new HashSet<>(List.of()); 52 53 // TODO(slotus): Add known patterns. 54 private static final Set<String> JUNIT3_CLASS_PATTERNS = new HashSet<>(List.of()); 55 ClassProfile(String moduleName, String packageName, String className, boolean apiClass)56 public ClassProfile(String moduleName, String packageName, String className, boolean apiClass) { 57 mModule = moduleName; 58 mClass = className; 59 mPackage = packageName; 60 if (apiClass) { 61 mClassType |= ClassType.API.getValue(); 62 } 63 } 64 65 /** Representation of the class type. */ 66 public enum ClassType { 67 INTERFACE(1), 68 ABSTRACT(2), 69 JUNIT3(4), 70 JUNIT4(8), 71 ANNOTATION(16), 72 /** A non-test and non-annotation class.*/ 73 COMMON(32), 74 API(64); 75 76 private final int mValue; 77 ClassType(int value)78 ClassType(int value) { 79 mValue = value; 80 } 81 getValue()82 public int getValue() { 83 return mValue; 84 } 85 } 86 getClassSignature()87 public String getClassSignature() { 88 return Utils.getClassSignature(mPackage, mClass); 89 } 90 getClassName()91 public String getClassName() { 92 return mClass; 93 } 94 getPackageName()95 public String getPackageName() { 96 return mPackage; 97 } 98 getModuleName()99 public String getModuleName() { 100 return mModule; 101 } 102 getMethods()103 public Map<String, MethodProfile> getMethods() { 104 return mMethods; 105 } 106 107 /** Creates a class method. */ getOrCreateMethod( String methodName, List<String> params)108 public MethodProfile getOrCreateMethod( 109 String methodName, List<String> params) { 110 String methodSignature = Utils.getMethodSignature(methodName, params); 111 if (!mMethods.containsKey(methodSignature)) { 112 mMethods.put(methodSignature, new MethodProfile(this, methodName, params)); 113 } 114 return mMethods.get(methodSignature); 115 } 116 117 /** Adds an interface implemented by the class. */ addInterface(ClassProfile interfaceProfile)118 public void addInterface(ClassProfile interfaceProfile) { 119 mInterfaces.add(interfaceProfile); 120 } 121 122 /** Adds a class type for the class. */ addClassType(ClassType classType)123 public void addClassType(ClassType classType) { 124 mClassType |= classType.getValue(); 125 } 126 setSuperClass(ClassProfile superClass)127 public void setSuperClass(ClassProfile superClass) { 128 mSuperClass = superClass; 129 } 130 131 /** Collects all test methods contained in the class. */ getTestMethods()132 public Map<String, MethodProfile> getTestMethods() { 133 if (mTestMethods != null) { 134 return mTestMethods; 135 } 136 mTestMethods = new HashMap<>(); 137 mMethods.forEach((methodKey, method) -> { 138 if (method.isTestMethod()) { 139 mTestMethods.put(methodKey, method); 140 } 141 }); 142 // Test methods defined in the super class will also be collected. 143 if (mSuperClass != null) { 144 mSuperClass.getTestMethods().forEach(mTestMethods::putIfAbsent); 145 } 146 return mTestMethods; 147 } 148 149 /** Returns true if the class is a test class. */ isTestClass()150 public boolean isTestClass() { 151 if (matchAnyTypes(ClassType.ANNOTATION.getValue() | ClassType.API.getValue())) { 152 return false; 153 } 154 if (!isJunit4Class() && !isJunit3Class()) { 155 addClassType(ClassType.COMMON); 156 return false; 157 } 158 return true; 159 } 160 161 /** Returns true if the class is an API class. */ isApiClass()162 public boolean isApiClass() { 163 return matchAllTypes(ClassType.API.getValue()); 164 } 165 166 /** Returns true if the class is a test class but not an abstract class. */ isNonAbstractTestClass()167 public boolean isNonAbstractTestClass() { 168 return (isTestClass() && !matchAnyTypes( 169 ClassType.ABSTRACT.getValue() | ClassType.INTERFACE.getValue())); 170 } 171 172 173 /** Returns true if it is decided that whether this is a test class or not. */ testClassResolved()174 private boolean testClassResolved() { 175 return matchAnyTypes( 176 ClassType.JUNIT3.getValue() 177 | ClassType.JUNIT4.getValue() 178 | ClassType.COMMON.getValue() 179 | ClassType.ANNOTATION.getValue() 180 | ClassType.API.getValue() 181 ); 182 } 183 184 /** Returns true if the class is a Junit4 test class. */ isJunit4Class()185 protected boolean isJunit4Class() { 186 if (testClassResolved()) { 187 return matchAllTypes(ClassType.JUNIT4.getValue()); 188 } 189 // Check if the class is marked by a Junit4 runner. 190 for (ClassProfile annotation : annotationManagement.getAnnotations()) { 191 for (String pattern : JUNIT4_ANNOTATION_PATTERNS) { 192 if (annotation.getClassSignature().matches(pattern)) { 193 addClassType(ClassType.JUNIT4); 194 return true; 195 } 196 } 197 } 198 // Check if any methods are marked by a Junit4 annotation. 199 for (MethodProfile method : mMethods.values()) { 200 if (method.isJunit4Method()) { 201 addClassType(ClassType.JUNIT4); 202 return true; 203 } 204 } 205 // Check if the class is extended from a Junit4 class. 206 if (mSuperClass != null && mSuperClass.isJunit4Class()) { 207 addClassType(ClassType.JUNIT4); 208 return true; 209 } 210 return false; 211 } 212 213 /** Returns true if the class is a Junit3 test class. */ isJunit3Class()214 protected boolean isJunit3Class() { 215 if (testClassResolved()) { 216 return matchAllTypes(ClassType.JUNIT3.getValue()); 217 } 218 if (mSuperClass != null) { 219 // Check if the class is extended from a Junit3 base class. 220 for (String pattern : JUNIT3_CLASS_PATTERNS) { 221 if (mSuperClass.getClassSignature().matches(pattern)) { 222 addClassType(ClassType.JUNIT3); 223 return true; 224 } 225 } 226 // Check if the class is extended from a Junit3 class. 227 if (mSuperClass.isJunit3Class()) { 228 addClassType(ClassType.JUNIT3); 229 return true; 230 } 231 } 232 return false; 233 } 234 235 matchAnyTypes(int typesValue)236 private boolean matchAnyTypes(int typesValue) { 237 return (mClassType & typesValue) != 0; 238 } 239 matchAllTypes(int typesValue)240 private boolean matchAllTypes(int typesValue) { 241 return (mClassType & typesValue) == typesValue; 242 } 243 } 244