• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.adservices.common.logging;
18 
19 import static com.android.adservices.common.logging.annotations.ExpectErrorLogUtilCall.ANNOTATION_NAME;
20 
21 import android.util.Log;
22 
23 import com.android.adservices.common.logging.annotations.ExpectErrorLogUtilCall;
24 import com.android.adservices.common.logging.annotations.ExpectErrorLogUtilCalls;
25 import com.android.adservices.common.logging.annotations.SetErrorLogUtilDefaultParams;
26 import com.android.adservices.shared.testing.AbstractLogVerifier;
27 import com.android.adservices.shared.testing.TestHelper;
28 
29 import com.google.common.collect.ImmutableList;
30 import com.google.common.collect.ImmutableSet;
31 
32 import org.junit.runner.Description;
33 
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Set;
37 import java.util.stream.Collectors;
38 
39 /**
40  * Log verifier for scanning usage of {@code ErrorLogUtil.e(int, int)} invocations. Uses {@link
41  * ErrorLogUtilSyncCallback} internally to capture log calls and waits for expected number of calls
42  * to be made before verifying or failing the test if expected number of calls are not made (default
43  * 5s timeout).
44  *
45  * <p>Use {@link ExpectErrorLogUtilCall} to verify both background and non-background logging calls.
46  */
47 public final class AdServicesErrorLogUtilVerifier extends AbstractLogVerifier<ErrorLogUtilCall> {
48     private ErrorLogUtilSyncCallback mErrorLogUtilSyncCallback;
49     private Set<ErrorLogUtilCall> mExpectedLogCallsCache;
50 
51     @Override
mockLogCalls(Description description)52     protected void mockLogCalls(Description description) {
53         // Configure the sync callback to await for expected number of calls to be made before
54         // verification. Note: default timeout is set to 5 seconds before the test fails.
55         mErrorLogUtilSyncCallback =
56                 ErrorLogUtilSyncCallback.mockErrorLogUtilWithoutThrowable(
57                         getTotalExpectedLogCalls(description));
58     }
59 
60     @Override
getExpectedLogCalls(Description description)61     public Set<ErrorLogUtilCall> getExpectedLogCalls(Description description) {
62         if (mExpectedLogCallsCache == null) {
63             // Parsing the expected calls is required at least twice: once for configuring the
64             // number of calls for the sync callback and once in the verification step after
65             // test execution. Expected log calls are cached so it's parsed only once.
66             mExpectedLogCallsCache = parseExpectedLogCalls(description);
67         }
68         return mExpectedLogCallsCache;
69     }
70 
71     @Override
getActualLogCalls()72     public Set<ErrorLogUtilCall> getActualLogCalls() {
73         if (mErrorLogUtilSyncCallback == null) {
74             throw new IllegalStateException(
75                     "mErrorLogUtilSyncCallback is null, which indicates mocking isn't done prior "
76                             + "to fetching the actual log calls.");
77         }
78 
79         return dedupeCalls(mErrorLogUtilSyncCallback.getResultsReceivedUponWaiting());
80     }
81 
82     @Override
getResolutionMessage()83     public String getResolutionMessage() {
84         return "Please make sure to use @"
85                 + ANNOTATION_NAME
86                 + "(..) "
87                 + "over test method to denote "
88                 + "all expected ErrorLogUtil.e(int, int) calls.";
89     }
90 
parseExpectedLogCalls(Description description)91     private Set<ErrorLogUtilCall> parseExpectedLogCalls(Description description) {
92         List<ExpectErrorLogUtilCall> annotations = getAnnotations(description);
93         SetErrorLogUtilDefaultParams defaultParams =
94                 TestHelper.getAnnotationFromTypesOnly(
95                         description.getTestClass(), SetErrorLogUtilDefaultParams.class);
96 
97         if (annotations.isEmpty()) {
98             Log.v(mTag, "No @" + ANNOTATION_NAME + " found over test method.");
99             return ImmutableSet.of();
100         }
101 
102         Set<ErrorLogUtilCall> expectedCalls =
103                 annotations.stream()
104                         .peek(a -> validateTimes(a.times(), ANNOTATION_NAME))
105                         .map(a -> ErrorLogUtilCall.createFrom(a, defaultParams))
106                         .collect(Collectors.toSet());
107 
108         if (expectedCalls.size() != annotations.size()) {
109             throw new IllegalStateException(
110                     "Detected @"
111                             + ANNOTATION_NAME
112                             + " annotations representing the same "
113                             + "invocation! De-dupe by using times arg");
114         }
115 
116         return expectedCalls;
117     }
118 
getAnnotations(Description description)119     private List<ExpectErrorLogUtilCall> getAnnotations(Description description) {
120         // Scan for multiple annotation container
121         ExpectErrorLogUtilCalls multiple = description.getAnnotation(ExpectErrorLogUtilCalls.class);
122         if (multiple != null) {
123             return Arrays.stream(multiple.value()).collect(Collectors.toList());
124         }
125 
126         // Scan for single annotation
127         ExpectErrorLogUtilCall single = description.getAnnotation(ExpectErrorLogUtilCall.class);
128         return single == null ? ImmutableList.of() : ImmutableList.of(single);
129     }
130 
getTotalExpectedLogCalls(Description description)131     private int getTotalExpectedLogCalls(Description description) {
132         return getExpectedLogCalls(description).stream().mapToInt(call -> call.mTimes).sum();
133     }
134 }
135