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.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 
25 /** Representation of a method included in the CTS package. */
26 public class MethodProfile {
27 
28     public final AnnotationManagement annotationManagement = new AnnotationManagement();
29 
30     private final String mMethod;
31 
32     private final ClassProfile mClass;
33 
34     private final List<String> mParams;
35 
36     private int mMethodType = 0;
37 
38     // A map of non-api methods called by this method with the method signature as the key.
39     private final HashMap<String, MethodProfile> mCommonMethodCalls = new HashMap<>();
40 
41     // A map of api methods called by this method with the method signature as the key.
42     private final HashMap<String, MethodProfile> mApiMethodCalls = new HashMap<>();
43 
44     // A map of api constructors called by this method with the method signature as the key.
45     private final HashMap<String, MethodProfile> mApiConstructorCalls = new HashMap<>();
46 
47     // TODO(slotus): Add known patterns.
48     private static final Set<String> JUNIT4_ANNOTATION_PATTERNS = new HashSet<>(List.of());
49 
50     public enum MethodType {
51         JUNIT3(1),
52         JUNIT4(2),
53         /** A non-test method.*/
54         COMMON(4),
55         // TODO(slotus): Solve the override case.
56         /** A method that is not extended from the super class. */
57         DIRECT_MEMBER(8);
58 
59         private final int mValue;
60 
MethodType(int value)61         MethodType(int value) {
62             mValue = value;
63         }
64 
getValue()65         public int getValue() {
66             return mValue;
67         }
68     }
69 
MethodProfile( ClassProfile classProfile, String methodName, List<String> params)70     public MethodProfile(
71             ClassProfile classProfile, String methodName, List<String> params) {
72         mClass = classProfile;
73         mMethod = methodName;
74         mParams = params;
75     }
76 
getMethodName()77     public String getMethodName() {
78         return mMethod;
79     }
80 
getModuleName()81     public String getModuleName() {
82         return mClass.getModuleName();
83     }
84 
getPackageName()85     public String getPackageName() {
86         return mClass.getPackageName();
87     }
88 
getClassName()89     public String getClassName() {
90         return mClass.getClassName();
91     }
92 
getMethodParams()93     public List<String> getMethodParams() {
94         return mParams;
95     }
96 
getApiMethodCalls()97     public Map<String, MethodProfile> getApiMethodCalls() {
98         return mApiMethodCalls;
99     }
100 
getApiConstructorCalls()101     public Map<String, MethodProfile> getApiConstructorCalls() {
102         return mApiConstructorCalls;
103     }
104 
getCommonMethodCalls()105     public Map<String, MethodProfile> getCommonMethodCalls() {
106         return mCommonMethodCalls;
107     }
108 
getMethodSignatureWithClass()109     public String getMethodSignatureWithClass() {
110         return Utils.getMethodSignatureWithClass(
111                 mClass.getPackageName(), mClass.getClassName(), mMethod, mParams);
112     }
113 
114     /** Adds an API method called by the method. **/
addApiMethodCall(MethodProfile apiMethod)115     public void addApiMethodCall(MethodProfile apiMethod) {
116         mApiMethodCalls.putIfAbsent(apiMethod.getMethodSignatureWithClass(), apiMethod);
117     }
118 
119     /** Adds an API constructor called by the method. **/
addApiConstructorCall(MethodProfile apiConstructor)120     public void addApiConstructorCall(MethodProfile apiConstructor) {
121         mApiConstructorCalls.putIfAbsent(
122                 apiConstructor.getMethodSignatureWithClass(), apiConstructor);
123     }
124 
125     /** Adds a non-API method called by the method. **/
addCommonMethodCall(MethodProfile method)126     public void addCommonMethodCall(MethodProfile method) {
127         mCommonMethodCalls.putIfAbsent(method.getMethodSignatureWithClass(), method);
128     }
129 
130     /** Adds a method type for the method. */
addMethodType(MethodType methodType)131     public void addMethodType(MethodType methodType) {
132         mMethodType |= methodType.getValue();
133     }
134 
135     /** Returns true if it is decided that whether this is a test method or not. */
testMethodResolved()136     private boolean testMethodResolved() {
137         return matchAnyTypes(
138                 MethodType.JUNIT3.getValue()
139                         | MethodType.JUNIT4.getValue()
140                         | MethodType.COMMON.getValue());
141     }
142 
143     /** Returns true if the method is a test method. */
isTestMethod()144     public boolean isTestMethod() {
145         if (!isJunit4Method() && !isJunit3Method()) {
146             addMethodType(MethodType.COMMON);
147             return false;
148         }
149         return true;
150     }
151 
152     /** Returns true if the method is a JUnit3 test method. */
isJunit3Method()153     protected boolean isJunit3Method() {
154         if (testMethodResolved()) {
155             return matchAllTypes(MethodType.JUNIT3.getValue());
156         }
157         if (mClass.isJunit3Class() && mMethod.startsWith("test")) {
158             addMethodType(MethodType.JUNIT3);
159             return true;
160         }
161         return false;
162     }
163 
164     /** Returns true if the method is a JUnit4 test method. */
isJunit4Method()165     protected boolean isJunit4Method() {
166         if (testMethodResolved()) {
167             return matchAllTypes(MethodType.JUNIT4.getValue());
168         }
169         for (ClassProfile annotation : annotationManagement.getAnnotations()) {
170             for (String pattern : JUNIT4_ANNOTATION_PATTERNS) {
171                 if (annotation.getClassSignature().matches(pattern)) {
172                     addMethodType(MethodType.JUNIT4);
173                     return true;
174                 }
175             }
176         }
177         return false;
178     }
179 
matchAnyTypes(int typesValue)180     private boolean matchAnyTypes(int typesValue) {
181         return (mMethodType & typesValue) != 0;
182     }
183 
matchAllTypes(int typesValue)184     private boolean matchAllTypes(int typesValue) {
185         return (mMethodType & typesValue) == typesValue;
186     }
187 }
188