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