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 package com.android.adservices.shared.testing.concurrency;
17 
18 import static com.android.adservices.shared.testing.concurrency.FailableResultSyncCallback.MSG_WRONG_ERROR_RECEIVED;
19 
20 import static org.junit.Assert.assertThrows;
21 
22 import com.android.adservices.shared.testing.Nullable;
23 
24 import org.junit.Test;
25 
26 import java.util.List;
27 
28 // TODO(b/342448771): make it package protected (must move some subclass to this package first)
29 /** Base test for classes that extend FailableResultSyncCallback. */
30 public abstract class FailableResultSyncCallbackTestCase<
31                 R, F, CB extends FailableResultSyncCallback<R, F>>
32         extends IResultSyncCallbackTestCase<R, CB> {
33 
34     /** Returns a new failure. */
newFailure()35     protected abstract F newFailure();
36 
37     /**
38      * Returns a class that is not the same as the class of objects returned by {@link
39      * #newFailure()} .
40      */
getClassOfDifferentFailure()41     protected abstract Class<?> getClassOfDifferentFailure();
42 
43     @Test
testNewFailureAndGetClassOfDifferentFailure()44     public final void testNewFailureAndGetClassOfDifferentFailure() {
45         Class<?> clazz1 = getClassOfDifferentFailure();
46         expect.withMessage("1st call to getClassOfdifferentFailure()").that(clazz1).isNotNull();
47 
48         Class<?> clazz2 = getClassOfDifferentFailure();
49         expect.withMessage("2nd call to getClassOfdifferentFailure()").that(clazz2).isNotNull();
50         expect.withMessage("2nd call to getClassOfdifferentFailure()")
51                 .that(clazz2)
52                 .isSameInstanceAs(clazz1);
53 
54         F failure1 = newFailure();
55         expect.withMessage("1st failure").that(failure1).isNotNull();
56         expect.withMessage("1st failure").that(failure1).isNotInstanceOf(clazz1);
57 
58         F failure2 = newFailure();
59         expect.withMessage("2nd failure").that(failure2).isNotNull();
60         expect.withMessage("2nd failure").that(failure2).isNotSameInstanceAs(failure1);
61         expect.withMessage("2nd failure").that(failure2).isNotInstanceOf(clazz1);
62     }
63 
64     @Test
testInjectResult_checkFailure()65     public final void testInjectResult_checkFailure() throws Exception {
66         assertInitialState(mCallback);
67         R injectedResult = null;
68 
69         runAsync(INJECTION_TIMEOUT_MS, () -> mCallback.injectResult(injectedResult));
70         mCallback.assertCalled();
71 
72         String when = "after injectFailure()";
73         assertGetResultMethods(mCallback, when, injectedResult);
74         assertGetFailureMethodsWhenNoFailure(mCallback, when);
75     }
76 
77     @Test
testInjectFailure_null()78     public final void testInjectFailure_null() {
79         assertThrows(NullPointerException.class, () -> mCallback.injectFailure(null));
80     }
81 
82     @Test
testInjectFailure()83     public final void testInjectFailure() throws Exception {
84         F failure = newFailure();
85         assertInitialState(mCallback);
86 
87         runAsync(INJECTION_TIMEOUT_MS, () -> mCallback.injectFailure(failure));
88 
89         F receivedFailure = mCallback.assertFailureReceived();
90 
91         expect.withMessage("%s.assertFailureReceived()", mCallback)
92                 .that(receivedFailure)
93                 .isSameInstanceAs(failure);
94         expect.withMessage("%s.isCalled() after assertFailureReceived()", mCallback)
95                 .that(mCallback.isCalled())
96                 .isTrue();
97 
98         String when = "after assertFailureReceived()";
99         assertGetFailureMethods(mCallback, when, failure);
100         assertGetResultMethodsWhenNoResult(mCallback, when);
101     }
102 
103     @Test
testInjectFailure_calledTwice()104     public final void testInjectFailure_calledTwice() throws Exception {
105         F failure = newFailure();
106         F anotherFailure = newFailure();
107         mCallback.injectFailure(failure);
108         mCallback.injectFailure(anotherFailure);
109 
110         mCallback.assertFailureReceived();
111 
112         String when = "after 2 injectFailure() calls";
113         assertGetResultMethodsWhenNoResult(mCallback, when);
114         assertGetFailureMethods(mCallback, when, failure, anotherFailure);
115     }
116 
117     @Test
testInjectFailure_calledAfterInjectResult()118     public final void testInjectFailure_calledAfterInjectResult() throws Exception {
119         String when = "after injectResult() and injectFailure()";
120         R result = null;
121         F failure = newFailure();
122         mCallback.injectResult(result);
123         mCallback.injectFailure(failure);
124 
125         mCallback.assertFailureReceived();
126 
127         assertGetResultMethods(mCallback, when, result);
128         assertGetFailureMethodsWhenInjectedResultWasCalledFirst(mCallback, when, failure);
129     }
130 
131     @Test
testInjectResult_calledAfterInjectFailure()132     public final void testInjectResult_calledAfterInjectFailure() throws Exception {
133         R result = null;
134         F failure = newFailure();
135         mCallback.injectFailure(failure);
136         mCallback.injectResult(result);
137 
138         F failureReceived = mCallback.assertFailureReceived();
139         expect.withMessage("%s.assertFailureReceived()", mCallback)
140                 .that(failureReceived)
141                 .isSameInstanceAs(failure);
142 
143         String when = "after injectFailure() and injectResult()";
144         assertGetFailureMethods(mCallback, when, failure);
145         assertGetResultMethods(mCallback, when, result);
146     }
147 
148     @Test
testAssertFailureReceived_null()149     public final void testAssertFailureReceived_null() throws Exception {
150         assertThrows(NullPointerException.class, () -> mCallback.assertFailureReceived(null));
151     }
152 
153     @Test
testAssertFailureReceived_rightClass()154     public final void testAssertFailureReceived_rightClass() throws Exception {
155         F failure = newFailure();
156         mCallback.injectFailure(failure);
157         @SuppressWarnings("unchecked")
158         Class<F> subFailureClass = (Class<F>) failure.getClass();
159 
160         F failureReceived = mCallback.assertFailureReceived(subFailureClass);
161 
162         expect.withMessage("%s.assertFailureReceived(...)", mCallback)
163                 .that(failureReceived)
164                 .isSameInstanceAs(failure);
165     }
166 
167     @Test
testAssertFailureReceived_wrongClass()168     public final void testAssertFailureReceived_wrongClass() throws Exception {
169         @SuppressWarnings("unchecked")
170         Class<F> subFailureClass = (Class<F>) getClassOfDifferentFailure();
171         F failure = newFailure();
172         mCallback.injectFailure(failure);
173 
174         IllegalStateException thrown =
175                 assertThrows(
176                         IllegalStateException.class,
177                         () -> mCallback.assertFailureReceived(subFailureClass));
178 
179         expect.withMessage("thrown")
180                 .that(thrown)
181                 .hasMessageThat()
182                 .isEqualTo(String.format(MSG_WRONG_ERROR_RECEIVED, subFailureClass, failure));
183     }
184 
185     @Test
testGetFailures_immutable()186     public final void testGetFailures_immutable() throws Exception {
187         List<F> failures = mCallback.getFailures();
188         expect.withMessage("%s.getFailures() initially", mCallback).that(failures).isEmpty();
189 
190         assertThrows(UnsupportedOperationException.class, () -> failures.add(newFailure()));
191 
192         expect.withMessage("%s.getFailures() after", mCallback)
193                 .that(mCallback.getFailures())
194                 .isEmpty();
195     }
196 
197     @Test
testToString_containsFailures()198     public final void testToString_containsFailures() throws Exception {
199         // Initial state
200         String toString = mCallback.toString();
201         expect.withMessage("toString() initially").that(toString).contains("(no failure yet)");
202 
203         // NOTE: failures are shown as "results" - see why on
204         // FailableResultSyncCallback.customizeToString()
205 
206         // 1st call
207         F firstFailure = newFailure();
208         mCallback.injectFailure(firstFailure);
209         toString = mCallback.toString();
210         expect.withMessage("toString() after 1st call")
211                 .that(toString)
212                 .contains("result=" + firstFailure);
213         expect.withMessage("toString() after 1st call")
214                 .that(toString)
215                 .contains("results=[" + firstFailure + "]");
216 
217         // 2nd call
218         F secondFailure = newFailure();
219         mCallback.injectFailure(secondFailure);
220         toString = mCallback.toString();
221         expect.withMessage("toString() after 2nd call")
222                 .that(toString)
223                 .contains("result=" + firstFailure);
224         expect.withMessage("toString() after 2nd call")
225                 .that(toString)
226                 .contains("results=[" + firstFailure + ", " + secondFailure + "]");
227     }
228 
assertGetFailureMethodsWhenNoFailure(CB callback, String when)229     protected final void assertGetFailureMethodsWhenNoFailure(CB callback, String when) {
230         expect.withMessage("%s.getFailure() %s", callback, when)
231                 .that(callback.getFailure())
232                 .isNull();
233         expect.withMessage("%s.getFailures() %s", callback, when)
234                 .that(callback.getFailures())
235                 .isEmpty();
236     }
237 
238     @SafeVarargs
assertGetFailureMethods( CB callback, String when, @Nullable F... failures)239     protected final void assertGetFailureMethods(
240             CB callback, String when, @Nullable F... failures) {
241         expect.withMessage("%s.getFailure() %s", callback, when)
242                 .that(callback.getFailure())
243                 .isSameInstanceAs(failures[0]);
244         expect.withMessage("%s.getFailures() %s", callback, when)
245                 .that(callback.getFailures())
246                 .containsExactly(failures)
247                 .inOrder();
248     }
249 
250     @SafeVarargs
assertGetFailureMethodsWhenInjectedResultWasCalledFirst( CB callback, String when, @Nullable F... failures)251     protected final void assertGetFailureMethodsWhenInjectedResultWasCalledFirst(
252             CB callback, String when, @Nullable F... failures) {
253         expect.withMessage("%s.getFailure() %s", callback, when)
254                 .that(callback.getFailure())
255                 .isNull();
256         expect.withMessage("%s.getFailures() %s", callback, when)
257                 .that(callback.getFailures())
258                 .containsExactly(failures)
259                 .inOrder();
260     }
261 
262     @SafeVarargs
assertGetResultMethodsWhenInjectFailureWasCalledFirst( CB callback, String when, @Nullable R... expectedResults)263     protected final void assertGetResultMethodsWhenInjectFailureWasCalledFirst(
264             CB callback, String when, @Nullable R... expectedResults) {
265         expect.withMessage("%s.getResult() %s", callback, when).that(callback.getResult()).isNull();
266         expect.withMessage("%s.getResults() %s", callback, when)
267                 .that(callback.getResults())
268                 .containsExactly(expectedResults)
269                 .inOrder();
270     }
271 
assertInitialState(CB callback)272     private void assertInitialState(CB callback) {
273         assertGetResultMethodsWhenNoResult(callback, "initially");
274         assertGetFailureMethodsWhenNoFailure(callback, "initially");
275     }
276 }
277