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