1 /* 2 * Copyright (C) 2010 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.apicoverage; 18 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.Collections; 22 import java.util.HashMap; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.Optional; 26 import java.util.Set; 27 28 /** Representation of a class in the API with constructors and methods. */ 29 class ApiClass implements Comparable<ApiClass>, HasCoverage { 30 31 private static final String VOID = "void"; 32 33 private final String mName; 34 35 private final boolean mDeprecated; 36 37 private final boolean mAbstract; 38 39 private final List<ApiConstructor> mApiConstructors = Collections.synchronizedList(new ArrayList<>()); 40 41 private final List<ApiMethod> mApiMethods = Collections.synchronizedList(new ArrayList<>()); 42 43 private final String mSuperClassName; 44 45 private ApiClass mSuperClass; 46 47 private Map<String, ApiClass> mInterfaceMap = new HashMap<String, ApiClass>(); 48 49 /** 50 * @param name The name of the class 51 * @param deprecated true iff the class is marked as deprecated 52 * @param classAbstract true iff the class is abstract 53 * @param superClassName The fully qualified name of the super class 54 */ ApiClass( String name, boolean deprecated, boolean classAbstract, String superClassName)55 ApiClass( 56 String name, 57 boolean deprecated, 58 boolean classAbstract, 59 String superClassName) { 60 mName = name; 61 mDeprecated = deprecated; 62 mAbstract = classAbstract; 63 mSuperClassName = superClassName; 64 } 65 66 @Override compareTo(ApiClass another)67 public int compareTo(ApiClass another) { 68 return mName.compareTo(another.mName); 69 } 70 71 @Override getName()72 public String getName() { 73 return mName; 74 } 75 isDeprecated()76 public boolean isDeprecated() { 77 return mDeprecated; 78 } 79 getSuperClassName()80 public String getSuperClassName() { 81 return mSuperClassName; 82 } 83 isAbstract()84 public boolean isAbstract() { 85 return mAbstract; 86 } 87 setSuperClass(ApiClass superClass)88 public void setSuperClass(ApiClass superClass) { mSuperClass = superClass; } 89 addInterface(String interfaceName)90 public void addInterface(String interfaceName) { 91 mInterfaceMap.put(interfaceName, null); 92 } 93 resolveInterface(String interfaceName, ApiClass apiInterface)94 public void resolveInterface(String interfaceName, ApiClass apiInterface) { 95 mInterfaceMap.replace(interfaceName, apiInterface); 96 } 97 getInterfaceNames()98 public Set<String> getInterfaceNames() { 99 return mInterfaceMap.keySet(); 100 } 101 addConstructor(ApiConstructor constructor)102 public void addConstructor(ApiConstructor constructor) { 103 mApiConstructors.add(constructor); 104 } 105 getConstructors()106 public Collection<ApiConstructor> getConstructors() { 107 return Collections.unmodifiableList(mApiConstructors); 108 } 109 addMethod(ApiMethod method)110 public void addMethod(ApiMethod method) { 111 mApiMethods.add(method); 112 } 113 114 /** Look for a matching constructor and mark it as covered */ markConstructorCovered(List<String> parameterTypes, String coveredbyApk)115 public void markConstructorCovered(List<String> parameterTypes, String coveredbyApk) { 116 if (mSuperClass != null) { 117 // Mark matching constructors in the superclass 118 mSuperClass.markConstructorCovered(parameterTypes, coveredbyApk); 119 } 120 Optional<ApiConstructor> apiConstructor = getConstructor(parameterTypes); 121 apiConstructor.ifPresent(constructor -> constructor.setCovered(coveredbyApk)); 122 } 123 124 /** Look for a matching method and if found and mark it as covered */ markMethodCovered(String name, List<String> parameterTypes, String coveredbyApk)125 public void markMethodCovered(String name, List<String> parameterTypes, String coveredbyApk) { 126 if (mSuperClass != null) { 127 // Mark matching methods in the super class 128 mSuperClass.markMethodCovered(name, parameterTypes, coveredbyApk); 129 } 130 if (!mInterfaceMap.isEmpty()) { 131 // Mark matching methods in the interfaces 132 for (ApiClass mInterface : mInterfaceMap.values()) { 133 if (mInterface != null) { 134 mInterface.markMethodCovered(name, parameterTypes, coveredbyApk); 135 } 136 } 137 } 138 Optional<ApiMethod> apiMethod = getMethod(name, parameterTypes); 139 apiMethod.ifPresent(method -> method.setCovered(coveredbyApk)); 140 } 141 getMethods()142 public Collection<ApiMethod> getMethods() { 143 return Collections.unmodifiableList(mApiMethods); 144 } 145 getNumCoveredMethods()146 public int getNumCoveredMethods() { 147 int numCovered = 0; 148 for (ApiConstructor constructor : mApiConstructors) { 149 if (constructor.isCovered()) { 150 numCovered++; 151 } 152 } 153 for (ApiMethod method : mApiMethods) { 154 if (method.isCovered()) { 155 numCovered++; 156 } 157 } 158 return numCovered; 159 } 160 getTotalMethods()161 public int getTotalMethods() { 162 return mApiConstructors.size() + mApiMethods.size(); 163 } 164 165 @Override getCoveragePercentage()166 public float getCoveragePercentage() { 167 if (getTotalMethods() == 0) { 168 return 100; 169 } else { 170 return (float) getNumCoveredMethods() / getTotalMethods() * 100; 171 } 172 } 173 174 @Override getMemberSize()175 public int getMemberSize() { 176 return getTotalMethods(); 177 } 178 getMethod(String name, List<String> parameterTypes)179 private Optional<ApiMethod> getMethod(String name, List<String> parameterTypes) { 180 for (ApiMethod method : mApiMethods) { 181 boolean methodNameMatch = name.equals(method.getName()); 182 boolean parameterTypeMatch = 183 compareParameterTypes(method.getParameterTypes(), parameterTypes); 184 if (methodNameMatch && parameterTypeMatch) { 185 return Optional.of(method); 186 } 187 } 188 return Optional.empty(); 189 } 190 191 /** 192 * The method compares two lists of parameters. If the {@code apiParameterTypeList} contains 193 * generic types, test parameter types are ignored. 194 * 195 * @param apiParameterTypeList The list of parameter types from the API 196 * @param testParameterTypeList The list of parameter types used in a test 197 * @return true iff the list of types are the same. 198 */ compareParameterTypes( List<String> apiParameterTypeList, List<String> testParameterTypeList)199 private static boolean compareParameterTypes( 200 List<String> apiParameterTypeList, List<String> testParameterTypeList) { 201 if (apiParameterTypeList.equals(testParameterTypeList)) { 202 return true; 203 } 204 if (apiParameterTypeList.size() != testParameterTypeList.size()) { 205 return false; 206 } 207 208 for (int i = 0; i < apiParameterTypeList.size(); i++) { 209 String apiParameterType = apiParameterTypeList.get(i); 210 String testParameterType = testParameterTypeList.get(i); 211 if (!compareType(apiParameterType, testParameterType)) { 212 return false; 213 } 214 } 215 return true; 216 } 217 218 /** 219 * @return true iff the parameter is a var arg parameter. 220 */ isVarArg(String parameter)221 private static boolean isVarArg(String parameter) { 222 return parameter.endsWith("..."); 223 } 224 225 /** 226 * Compare class types. 227 * @param apiType The type as reported by the api 228 * @param testType The type as found used in a test 229 * @return true iff the strings are equal, 230 * or the apiType is generic and the test type is not void 231 */ compareType(String apiType, String testType)232 private static boolean compareType(String apiType, String testType) { 233 return apiType.equals(testType) || 234 isGenericType(apiType) && !testType.equals(VOID) || 235 isGenericArrayType(apiType) && isArrayType(testType) || 236 isVarArg(apiType) && isArrayType(testType) && 237 apiType.startsWith(testType.substring(0, testType.indexOf("["))); 238 } 239 240 /** 241 * @return true iff the given parameterType is a generic type. 242 */ isGenericType(String type)243 private static boolean isGenericType(String type) { 244 return type.length() == 1 && 245 type.charAt(0) >= 'A' && 246 type.charAt(0) <= 'Z'; 247 } 248 249 /** 250 * @return true iff {@code type} ends with an []. 251 */ isArrayType(String type)252 private static boolean isArrayType(String type) { 253 return type.endsWith("[]"); 254 } 255 256 /** 257 * @return true iff the given parameterType is an array of generic type. 258 */ isGenericArrayType(String type)259 private static boolean isGenericArrayType(String type) { 260 return type.length() == 3 && isGenericType(type.substring(0, 1)) && isArrayType(type); 261 } 262 getConstructor(List<String> parameterTypes)263 private Optional<ApiConstructor> getConstructor(List<String> parameterTypes) { 264 for (ApiConstructor constructor : mApiConstructors) { 265 if (compareParameterTypes(constructor.getParameterTypes(), parameterTypes)) { 266 return Optional.of(constructor); 267 } 268 } 269 return Optional.empty(); 270 } 271 } 272