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.mockito;
17 
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.ArgumentMatchers.anyBoolean;
20 import static org.mockito.ArgumentMatchers.anyInt;
21 import static org.mockito.Mockito.doAnswer;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.when;
24 
25 import android.content.Context;
26 
27 import com.android.adservices.shared.common.ApplicationContextSingleton;
28 import com.android.adservices.shared.spe.logging.JobServiceLogger;
29 import com.android.adservices.shared.testing.JobServiceLoggingCallback;
30 import com.android.adservices.shared.util.Clock;
31 
32 import org.mockito.stubbing.OngoingStubbing;
33 
34 import java.util.Arrays;
35 import java.util.Objects;
36 
37 /** Implements {@link SharedMocker} using {@code Mockito}. */
38 public final class SharedMockitoMocker extends AbstractMocker implements SharedMocker {
39 
40     @Override
setApplicationContextSingleton()41     public Context setApplicationContextSingleton() {
42         Context context = mock(Context.class);
43         logV("setApplicationContextSingleton(): will use %s as appContext", context);
44 
45         when(context.getApplicationContext()).thenReturn(context);
46         ApplicationContextSingleton.setForTests(context);
47 
48         return context;
49     }
50 
51     @Override
mockSetApplicationContextSingleton(Context context)52     public void mockSetApplicationContextSingleton(Context context) {
53         Objects.requireNonNull(context, "context cannot be null");
54         logV("mockSetApplicationContextSingleton(%s)", context);
55 
56         ApplicationContextSingleton.setForTests(context);
57     }
58 
59     @Override
syncRecordOnStopJob(JobServiceLogger logger)60     public JobServiceLoggingCallback syncRecordOnStopJob(JobServiceLogger logger) {
61         JobServiceLoggingCallback callback = new JobServiceLoggingCallback();
62 
63         doAnswer(
64                         invocation -> {
65                             logV("calling callback %s on %s", callback, invocation);
66                             callback.onLoggingMethodCalled();
67                             return null;
68                         })
69                 .when(logger)
70                 .recordOnStopJob(any(), anyInt(), anyBoolean());
71 
72         return callback;
73     }
74 
75     @Override
mockCurrentTimeMillis(Clock mockClock, long... mockedValues)76     public void mockCurrentTimeMillis(Clock mockClock, long... mockedValues) {
77         logV("mockCurrentTimeMillis(%s, %s)", mockClock, Arrays.toString(mockedValues));
78         Objects.requireNonNull(mockClock, "mockClock cannot be null");
79         Objects.requireNonNull(mockedValues, "mockedValues cannot be null");
80 
81         unflatLongVararg(when(mockClock.currentTimeMillis()), mockedValues);
82     }
83 
84     @Override
mockElapsedRealtime(Clock mockClock, long... mockedValues)85     public void mockElapsedRealtime(Clock mockClock, long... mockedValues) {
86         logV("mockElapsedRealtime(%s, %s)", mockClock, Arrays.toString(mockedValues));
87         Objects.requireNonNull(mockClock, "mockClock cannot be null");
88         Objects.requireNonNull(mockedValues, "mockedValues cannot be null");
89 
90         unflatLongVararg(when(mockClock.elapsedRealtime()), mockedValues);
91     }
92 
93     // TODO(b/359358687): Move to MockitoHelper / rename / unit test it
94 
95     // Mockito's when() supporting passing a single value or a single value plus a vararg, but the
96     // signature of those methods take a T (object), so they cannot be used with primitive types.
97     // Hence, this method provides a "bridge" between helper methods that take long... and Mockito.
unflatLongVararg(OngoingStubbing<Long> mockedMethod, long... mockedValues)98     private static void unflatLongVararg(OngoingStubbing<Long> mockedMethod, long... mockedValues) {
99         Long firstValue = mockedValues[0];
100         if (mockedValues.length == 1) {
101             mockedMethod.thenReturn(firstValue);
102             return;
103         }
104 
105         int remainingLength = mockedValues.length - 1;
106         Long[] remainingValues = new Long[remainingLength];
107         for (int i = 0; i < remainingLength; i++) {
108             remainingValues[i] = mockedValues[i + 1];
109         }
110         mockedMethod.thenReturn(firstValue, remainingValues);
111     }
112 }
113