blob: 82f7e5efa79848f796fd47aba82b497c2504031f [file] [log] [blame]
Rui Qiu337cb242021-06-03 11:26:16 -07001/*
2 * Copyright (C) 2021 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
17package com.android.car.telemetry.databroker;
18
19import static com.google.common.truth.Truth.assertThat;
Rui Qiu033041e2021-06-21 14:59:11 -070020import static com.google.common.truth.Truth.assertWithMessage;
Rui Qiu337cb242021-06-03 11:26:16 -070021
Rui Qiua86ab602021-07-02 10:40:36 -070022import static org.mockito.ArgumentMatchers.any;
23import static org.mockito.ArgumentMatchers.anyInt;
24import static org.mockito.ArgumentMatchers.anyString;
Rui Qiuaba02082021-08-16 16:24:00 -070025import static org.mockito.ArgumentMatchers.eq;
Rui Qiua86ab602021-07-02 10:40:36 -070026import static org.mockito.Mockito.verify;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070027import static org.mockito.Mockito.when;
Rui Qiu337cb242021-06-03 11:26:16 -070028
Rui Qiua86ab602021-07-02 10:40:36 -070029import android.annotation.Nullable;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070030import android.car.hardware.CarPropertyConfig;
Rui Qiua86ab602021-07-02 10:40:36 -070031import android.content.Context;
32import android.content.ServiceConnection;
Rui Qiu033041e2021-06-21 14:59:11 -070033import android.os.Handler;
Rui Qiua86ab602021-07-02 10:40:36 -070034import android.os.IBinder;
Max Dashouk6987e9c2021-09-20 13:19:30 -070035import android.os.ParcelFileDescriptor;
Rui Qiuaba02082021-08-16 16:24:00 -070036import android.os.PersistableBundle;
Rui Qiua86ab602021-07-02 10:40:36 -070037import android.os.RemoteException;
Rui Qiu033041e2021-06-21 14:59:11 -070038import android.os.SystemClock;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070039
40import com.android.car.CarPropertyService;
Rui Qiuaba02082021-08-16 16:24:00 -070041import com.android.car.telemetry.ResultStore;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070042import com.android.car.telemetry.TelemetryProto;
43import com.android.car.telemetry.publisher.PublisherFactory;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070044import com.android.car.telemetry.publisher.StatsManagerProxy;
Max Dashouk9912ac32021-08-13 17:54:51 +000045import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutor;
46import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutorListener;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070047
48import org.junit.Before;
Rui Qiu337cb242021-06-03 11:26:16 -070049import org.junit.Test;
50import org.junit.runner.RunWith;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070051import org.mockito.Mock;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070052import org.mockito.Mockito;
Rui Qiuaf750102021-09-17 13:27:23 -070053import org.mockito.invocation.InvocationOnMock;
Rui Qiu337cb242021-06-03 11:26:16 -070054import org.mockito.junit.MockitoJUnitRunner;
Rui Qiuaf750102021-09-17 13:27:23 -070055import org.mockito.stubbing.Answer;
Rui Qiu337cb242021-06-03 11:26:16 -070056
Rui Qiuda9f0ba2021-09-14 12:15:37 -070057import java.nio.file.Files;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070058import java.util.Collections;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070059import java.util.concurrent.CountDownLatch;
Rui Qiu033041e2021-06-21 14:59:11 -070060import java.util.concurrent.PriorityBlockingQueue;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070061import java.util.concurrent.TimeUnit;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070062
Rui Qiu337cb242021-06-03 11:26:16 -070063@RunWith(MockitoJUnitRunner.class)
Rui Qiuac8b5ba2021-08-25 16:04:17 -070064public class DataBrokerTest {
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070065 private static final int PROP_ID = 100;
66 private static final int PROP_AREA = 200;
Rui Qiu033041e2021-06-21 14:59:11 -070067 private static final int PRIORITY_HIGH = 1;
Rui Qiud95f95d2021-08-17 12:29:33 -070068 private static final int PRIORITY_LOW = 100;
Rui Qiu033041e2021-06-21 14:59:11 -070069 private static final long TIMEOUT_MS = 5_000L;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070070 private static final CarPropertyConfig<Integer> PROP_CONFIG =
71 CarPropertyConfig.newBuilder(Integer.class, PROP_ID, PROP_AREA).setAccess(
72 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ).build();
Rui Qiu337cb242021-06-03 11:26:16 -070073 private static final TelemetryProto.VehiclePropertyPublisher
74 VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION =
75 TelemetryProto.VehiclePropertyPublisher.newBuilder().setReadRate(
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070076 1).setVehiclePropertyId(PROP_ID).build();
Rui Qiu337cb242021-06-03 11:26:16 -070077 private static final TelemetryProto.Publisher PUBLISHER_CONFIGURATION =
78 TelemetryProto.Publisher.newBuilder().setVehicleProperty(
79 VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION).build();
80 private static final TelemetryProto.Subscriber SUBSCRIBER_FOO =
81 TelemetryProto.Subscriber.newBuilder().setHandler("function_name_foo").setPublisher(
Rui Qiud95f95d2021-08-17 12:29:33 -070082 PUBLISHER_CONFIGURATION).setPriority(PRIORITY_HIGH).build();
Rui Qiu337cb242021-06-03 11:26:16 -070083 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_FOO =
84 TelemetryProto.MetricsConfig.newBuilder().setName("Foo").setVersion(
85 1).addSubscribers(SUBSCRIBER_FOO).build();
86 private static final TelemetryProto.Subscriber SUBSCRIBER_BAR =
87 TelemetryProto.Subscriber.newBuilder().setHandler("function_name_bar").setPublisher(
Rui Qiud95f95d2021-08-17 12:29:33 -070088 PUBLISHER_CONFIGURATION).setPriority(PRIORITY_LOW).build();
Rui Qiu337cb242021-06-03 11:26:16 -070089 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_BAR =
90 TelemetryProto.MetricsConfig.newBuilder().setName("Bar").setVersion(
91 1).addSubscribers(SUBSCRIBER_BAR).build();
92
Rui Qiuea6a4942021-09-07 15:05:34 -070093
94 // when count reaches 0, all handler messages are scheduled to be dispatched after current time
Rui Qiuac8b5ba2021-08-25 16:04:17 -070095 private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
Rui Qiuea6a4942021-09-07 15:05:34 -070096 private PersistableBundle mData = new PersistableBundle();
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070097 private DataBrokerImpl mDataBroker;
Rui Qiua86ab602021-07-02 10:40:36 -070098 private FakeScriptExecutor mFakeScriptExecutor;
Rui Qiu033041e2021-06-21 14:59:11 -070099 private ScriptExecutionTask mHighPriorityTask;
100 private ScriptExecutionTask mLowPriorityTask;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700101
Rui Qiua86ab602021-07-02 10:40:36 -0700102 @Mock
103 private Context mMockContext;
104 @Mock
105 private CarPropertyService mMockCarPropertyService;
106 @Mock
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700107 private Handler mMockHandler;
108 @Mock
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700109 private StatsManagerProxy mMockStatsManager;
110 @Mock
Rui Qiua86ab602021-07-02 10:40:36 -0700111 private IBinder mMockScriptExecutorBinder;
Rui Qiuaba02082021-08-16 16:24:00 -0700112 @Mock
113 private ResultStore mMockResultStore;
Rui Qiua86ab602021-07-02 10:40:36 -0700114
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700115 @Before
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700116 public void setUp() throws Exception {
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700117 when(mMockCarPropertyService.getPropertyList())
118 .thenReturn(Collections.singletonList(PROP_CONFIG));
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700119 PublisherFactory factory = new PublisherFactory(
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700120 mMockCarPropertyService, mMockHandler, mMockStatsManager,
121 Files.createTempDirectory("telemetry_test").toFile());
Rui Qiuaba02082021-08-16 16:24:00 -0700122 mDataBroker = new DataBrokerImpl(mMockContext, factory, mMockResultStore);
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700123 // add IdleHandler to get notified when all messages and posts are handled
124 mDataBroker.getTelemetryHandler().getLooper().getQueue().addIdleHandler(() -> {
125 mIdleHandlerLatch.countDown();
126 return true;
127 });
Rui Qiua86ab602021-07-02 10:40:36 -0700128
129 mFakeScriptExecutor = new FakeScriptExecutor();
130 when(mMockScriptExecutorBinder.queryLocalInterface(anyString()))
131 .thenReturn(mFakeScriptExecutor);
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700132 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(i -> {
133 ServiceConnection conn = i.getArgument(1);
134 conn.onServiceConnected(null, mMockScriptExecutorBinder);
135 return true;
136 });
Rui Qiua86ab602021-07-02 10:40:36 -0700137
Rui Qiu033041e2021-06-21 14:59:11 -0700138 mHighPriorityTask = new ScriptExecutionTask(
Rui Qiud95f95d2021-08-17 12:29:33 -0700139 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
Rui Qiuea6a4942021-09-07 15:05:34 -0700140 mData,
Rui Qiu033041e2021-06-21 14:59:11 -0700141 SystemClock.elapsedRealtime());
142 mLowPriorityTask = new ScriptExecutionTask(
Rui Qiud95f95d2021-08-17 12:29:33 -0700143 new DataSubscriber(mDataBroker, METRICS_CONFIG_BAR, SUBSCRIBER_BAR),
Rui Qiuea6a4942021-09-07 15:05:34 -0700144 mData,
Rui Qiu033041e2021-06-21 14:59:11 -0700145 SystemClock.elapsedRealtime());
146 }
147
148 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700149 public void testSetTaskExecutionPriority_whenNoTask_shouldNotInvokeScriptExecutor()
150 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700151 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
152
Rui Qiua86ab602021-07-02 10:40:36 -0700153 waitForHandlerThreadToFinish();
154 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700155 }
156
157 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700158 public void testSetTaskExecutionPriority_whenNextTaskPriorityLow_shouldNotRunTask()
159 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700160 mDataBroker.getTaskQueue().add(mLowPriorityTask);
161
162 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
163
164 waitForHandlerThreadToFinish();
165 // task is not polled
166 assertThat(mDataBroker.getTaskQueue().peek()).isEqualTo(mLowPriorityTask);
Rui Qiua86ab602021-07-02 10:40:36 -0700167 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700168 }
169
170 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700171 public void testSetTaskExecutionPriority_whenNextTaskPriorityHigh_shouldInvokeScriptExecutor()
172 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700173 mDataBroker.getTaskQueue().add(mHighPriorityTask);
174
175 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
176
177 waitForHandlerThreadToFinish();
178 // task is polled and run
179 assertThat(mDataBroker.getTaskQueue().peek()).isNull();
Rui Qiua86ab602021-07-02 10:40:36 -0700180 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
Rui Qiu033041e2021-06-21 14:59:11 -0700181 }
182
183 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700184 public void testScheduleNextTask_whenNoTask_shouldNotInvokeScriptExecutor() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700185 mDataBroker.scheduleNextTask();
186
Rui Qiua86ab602021-07-02 10:40:36 -0700187 waitForHandlerThreadToFinish();
188 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700189 }
190
191 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700192 public void testScheduleNextTask_whenTaskInProgress_shouldNotInvokeScriptExecutorAgain()
193 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700194 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
195 taskQueue.add(mHighPriorityTask);
196 mDataBroker.scheduleNextTask(); // start a task
197 waitForHandlerThreadToFinish();
198 assertThat(taskQueue.peek()).isNull(); // assert that task is polled and running
199 taskQueue.add(mHighPriorityTask); // add another task into the queue
200
201 mDataBroker.scheduleNextTask(); // schedule next task while the last task is in progress
202
Rui Qiuea6a4942021-09-07 15:05:34 -0700203 waitForHandlerThreadToFinish();
Rui Qiua86ab602021-07-02 10:40:36 -0700204 // verify task is not polled
205 assertThat(taskQueue.peek()).isEqualTo(mHighPriorityTask);
206 // expect one invocation for the task that is running
207 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
208 }
209
210 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700211 public void testScheduleNextTask_whenTaskCompletes_shouldAutomaticallyScheduleNextTask()
212 throws Exception {
Rui Qiua86ab602021-07-02 10:40:36 -0700213 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
214 // add two tasks into the queue for execution
215 taskQueue.add(mHighPriorityTask);
216 taskQueue.add(mHighPriorityTask);
217
218 mDataBroker.scheduleNextTask(); // start a task
219 waitForHandlerThreadToFinish();
220 // end a task, should automatically schedule the next task
Rui Qiuea6a4942021-09-07 15:05:34 -0700221 mFakeScriptExecutor.notifyScriptSuccess(mData); // posts to telemetry handler
Rui Qiua86ab602021-07-02 10:40:36 -0700222
223 waitForHandlerThreadToFinish();
224 // verify queue is empty, both tasks are polled and executed
225 assertThat(taskQueue.peek()).isNull();
226 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(2);
227 }
228
229 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700230 public void testScheduleNextTask_onScriptSuccess_shouldStoreInterimResult() throws Exception {
Rui Qiuea6a4942021-09-07 15:05:34 -0700231 mData.putBoolean("script is finished", false);
232 mData.putDouble("value of euler's number", 2.71828);
Rui Qiuaba02082021-08-16 16:24:00 -0700233 mDataBroker.getTaskQueue().add(mHighPriorityTask);
Rui Qiuaba02082021-08-16 16:24:00 -0700234
235 mDataBroker.scheduleNextTask();
236 waitForHandlerThreadToFinish();
Rui Qiuea6a4942021-09-07 15:05:34 -0700237 mFakeScriptExecutor.notifyScriptSuccess(mData); // posts to telemetry handler
Rui Qiuaba02082021-08-16 16:24:00 -0700238
Rui Qiuea6a4942021-09-07 15:05:34 -0700239 waitForHandlerThreadToFinish();
Rui Qiuaba02082021-08-16 16:24:00 -0700240 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700241 verify(mMockResultStore).putInterimResult(
242 eq(mHighPriorityTask.getMetricsConfig().getName()), eq(mData));
243 }
244
245 @Test
Rui Qiu862d6ea2021-08-17 15:00:09 -0700246 public void testScheduleNextTask_onScriptError_shouldStoreErrorObject() throws Exception {
247 mDataBroker.getTaskQueue().add(mHighPriorityTask);
248 TelemetryProto.TelemetryError.ErrorType errorType =
249 TelemetryProto.TelemetryError.ErrorType.LUA_RUNTIME_ERROR;
250 String errorMessage = "test onError";
251 TelemetryProto.TelemetryError expectedError = TelemetryProto.TelemetryError.newBuilder()
252 .setErrorType(errorType)
253 .setMessage(errorMessage)
254 .build();
255
256 mDataBroker.scheduleNextTask();
257 waitForHandlerThreadToFinish();
258 mFakeScriptExecutor.notifyScriptError(errorType.getNumber(), errorMessage);
259
260 waitForHandlerThreadToFinish();
261 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
262 verify(mMockResultStore).putError(eq(METRICS_CONFIG_FOO.getName()), eq(expectedError));
263 }
264
265 @Test
Rui Qiuea6a4942021-09-07 15:05:34 -0700266 public void testScheduleNextTask_whenScriptFinishes_shouldStoreFinalResult()
267 throws Exception {
268 mData.putBoolean("script is finished", true);
269 mData.putDouble("value of pi", 3.14159265359);
270 mDataBroker.getTaskQueue().add(mHighPriorityTask);
271
272 mDataBroker.scheduleNextTask();
273 waitForHandlerThreadToFinish();
274 mFakeScriptExecutor.notifyScriptFinish(mData); // posts to telemetry handler
275
276 waitForHandlerThreadToFinish();
277 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
278 verify(mMockResultStore).putFinalResult(
279 eq(mHighPriorityTask.getMetricsConfig().getName()), eq(mData));
280 }
281
282 @Test
283 public void testScheduleNextTask_whenInterimDataExists_shouldPassToScriptExecutor()
284 throws Exception {
285 mData.putDouble("value of golden ratio", 1.618033);
286 mDataBroker.getTaskQueue().add(mHighPriorityTask);
287 when(mMockResultStore.getInterimResult(mHighPriorityTask.getMetricsConfig().getName()))
288 .thenReturn(mData);
289
290 mDataBroker.scheduleNextTask();
291
292 waitForHandlerThreadToFinish();
293 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
294 assertThat(mFakeScriptExecutor.getSavedState()).isEqualTo(mData);
Rui Qiuaba02082021-08-16 16:24:00 -0700295 }
296
297 @Test
Rui Qiuaf750102021-09-17 13:27:23 -0700298 public void testScheduleNextTask_bindScriptExecutorFailedOnce_shouldRebind()
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700299 throws Exception {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700300 Mockito.reset(mMockContext);
Rui Qiuaf750102021-09-17 13:27:23 -0700301 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(
302 new Answer() {
303 private int mCount = 0;
304
305 @Override
306 public Object answer(InvocationOnMock invocation) {
307 if (mCount++ == 1) {
308 return false; // fail first attempt
309 }
310 ServiceConnection conn = invocation.getArgument(1);
311 conn.onServiceConnected(null, mMockScriptExecutorBinder);
312 return true; // second attempt should succeed
313 }
314 });
315 mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
Rui Qiua86ab602021-07-02 10:40:36 -0700316 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
317 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
318 taskQueue.add(mHighPriorityTask);
Rui Qiua86ab602021-07-02 10:40:36 -0700319
320 // will rebind to ScriptExecutor if it is null
321 mDataBroker.scheduleNextTask();
322
323 waitForHandlerThreadToFinish();
Rui Qiuaf750102021-09-17 13:27:23 -0700324 assertThat(taskQueue.peek()).isNull();
325 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
326 }
327
328 @Test
329 public void testScheduleNextTask_bindScriptExecutorFailedMultipleTimes_shouldDisableBroker()
330 throws Exception {
331 // fail 6 future attempts to bind to it
332 Mockito.reset(mMockContext);
333 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any()))
334 .thenReturn(false, false, false, false, false, false);
335 mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
336 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
337 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
338 taskQueue.add(mHighPriorityTask);
339
340 // will rebind to ScriptExecutor if it is null
341 mDataBroker.scheduleNextTask();
342
343 waitForHandlerThreadToFinish();
344 // broker disabled, all subscribers should have been removed
Rui Qiua86ab602021-07-02 10:40:36 -0700345 assertThat(mDataBroker.getSubscriptionMap()).hasSize(0);
346 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(0);
347 }
348
349 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700350 public void testScheduleNextTask_whenScriptExecutorThrowsException_shouldRequeueTask()
351 throws Exception {
Rui Qiua86ab602021-07-02 10:40:36 -0700352 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
353 taskQueue.add(mHighPriorityTask);
354 mFakeScriptExecutor.failNextApiCalls(1); // fail the next invokeScript() call
355
356 mDataBroker.scheduleNextTask();
357
358 waitForHandlerThreadToFinish();
359 // expect invokeScript() to be called and failed, causing the same task to be re-queued
360 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
Rui Qiu033041e2021-06-21 14:59:11 -0700361 assertThat(taskQueue.peek()).isEqualTo(mHighPriorityTask);
362 }
363
364 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700365 public void testAddTaskToQueue_shouldInvokeScriptExecutor() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700366 mDataBroker.addTaskToQueue(mHighPriorityTask);
367
Rui Qiua86ab602021-07-02 10:40:36 -0700368 waitForHandlerThreadToFinish();
369 assertThat(mFakeScriptExecutor.getApiInvocationCount()).isEqualTo(1);
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700370 }
371
Rui Qiu337cb242021-06-03 11:26:16 -0700372 @Test
373 public void testAddMetricsConfiguration_newMetricsConfig() {
Rui Qiuf14c6a22021-07-14 10:39:16 -0700374 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
375
Rui Qiuf14c6a22021-07-14 10:39:16 -0700376 assertThat(mDataBroker.getSubscriptionMap()).hasSize(1);
377 assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_BAR.getName());
378 // there should be one data subscriber in the subscription list of METRICS_CONFIG_BAR
379 assertThat(mDataBroker.getSubscriptionMap().get(METRICS_CONFIG_BAR.getName())).hasSize(1);
380 }
381
382
383 @Test
384 public void testAddMetricsConfiguration_duplicateMetricsConfig_shouldDoNothing() {
385 // this metrics config has already been added in setUp()
Rui Qiu337cb242021-06-03 11:26:16 -0700386 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
387
Rui Qiuf14c6a22021-07-14 10:39:16 -0700388 assertThat(mDataBroker.getSubscriptionMap()).hasSize(1);
Rui Qiu337cb242021-06-03 11:26:16 -0700389 assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_FOO.getName());
Rui Qiu337cb242021-06-03 11:26:16 -0700390 assertThat(mDataBroker.getSubscriptionMap().get(METRICS_CONFIG_FOO.getName())).hasSize(1);
391 }
392
393 @Test
Rui Qiuf14c6a22021-07-14 10:39:16 -0700394 public void testRemoveMetricsConfiguration_shouldRemoveAllAssociatedTasks() {
Rui Qiu337cb242021-06-03 11:26:16 -0700395 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
396 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
Rui Qiud95f95d2021-08-17 12:29:33 -0700397 ScriptExecutionTask taskWithMetricsConfigFoo = new ScriptExecutionTask(
398 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
Rui Qiuea6a4942021-09-07 15:05:34 -0700399 mData,
Rui Qiuf14c6a22021-07-14 10:39:16 -0700400 SystemClock.elapsedRealtime());
401 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
402 taskQueue.add(mHighPriorityTask); // associated with METRICS_CONFIG_FOO
Rui Qiud95f95d2021-08-17 12:29:33 -0700403 taskQueue.add(mLowPriorityTask); // associated with METRICS_CONFIG_BAR
404 taskQueue.add(taskWithMetricsConfigFoo); // associated with METRICS_CONFIG_FOO
Rui Qiuf14c6a22021-07-14 10:39:16 -0700405 assertThat(taskQueue).hasSize(3);
406
407 mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_FOO);
Rui Qiu337cb242021-06-03 11:26:16 -0700408
Rui Qiuf14c6a22021-07-14 10:39:16 -0700409 assertThat(taskQueue).hasSize(1);
Rui Qiud95f95d2021-08-17 12:29:33 -0700410 assertThat(taskQueue.poll()).isEqualTo(mLowPriorityTask);
Rui Qiuf14c6a22021-07-14 10:39:16 -0700411 }
412
413 @Test
414 public void testRemoveMetricsConfiguration_whenMetricsConfigNonExistent_shouldDoNothing() {
415 mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_BAR);
416
Rui Qiuf14c6a22021-07-14 10:39:16 -0700417 assertThat(mDataBroker.getSubscriptionMap()).hasSize(0);
Rui Qiu337cb242021-06-03 11:26:16 -0700418 }
419
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700420 private void waitForHandlerThreadToFinish() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700421 assertWithMessage("handler not idle in %sms", TIMEOUT_MS)
Rui Qiua4a8fd02021-09-02 19:08:40 -0700422 .that(mIdleHandlerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
Rui Qiuea6a4942021-09-07 15:05:34 -0700423 mIdleHandlerLatch = new CountDownLatch(1); // reset idle handler condition
Rui Qiu337cb242021-06-03 11:26:16 -0700424 }
Rui Qiua86ab602021-07-02 10:40:36 -0700425
426 private static class FakeScriptExecutor implements IScriptExecutor {
427 private IScriptExecutorListener mListener;
428 private int mApiInvocationCount = 0;
429 private int mFailApi = 0;
Rui Qiuea6a4942021-09-07 15:05:34 -0700430 private PersistableBundle mSavedState = null;
Rui Qiua86ab602021-07-02 10:40:36 -0700431
432 @Override
Max Dashoukdafdd222021-08-20 10:42:25 -0700433 public void invokeScript(String scriptBody, String functionName,
434 PersistableBundle publishedData, @Nullable PersistableBundle savedState,
435 IScriptExecutorListener listener)
Rui Qiua86ab602021-07-02 10:40:36 -0700436 throws RemoteException {
437 mApiInvocationCount++;
Rui Qiuea6a4942021-09-07 15:05:34 -0700438 mSavedState = savedState;
Rui Qiua86ab602021-07-02 10:40:36 -0700439 mListener = listener;
440 if (mFailApi > 0) {
441 mFailApi--;
442 throw new RemoteException("Simulated failure");
443 }
444 }
445
446 @Override
Max Dashouk6987e9c2021-09-20 13:19:30 -0700447 public void invokeScriptForLargeInput(String scriptBody, String functionName,
448 ParcelFileDescriptor publishedDataFileDescriptor,
449 @Nullable PersistableBundle savedState,
450 IScriptExecutorListener listener) throws RemoteException {
451 mApiInvocationCount++;
452 mSavedState = savedState;
453 mListener = listener;
454 if (mFailApi > 0) {
455 mFailApi--;
456 throw new RemoteException("Simulated failure");
457 }
458 }
459
460 @Override
Rui Qiua86ab602021-07-02 10:40:36 -0700461 public IBinder asBinder() {
462 return null;
463 }
464
Rui Qiuaba02082021-08-16 16:24:00 -0700465 /** Mocks script temporary completion. */
Max Dashoukdafdd222021-08-20 10:42:25 -0700466 public void notifyScriptSuccess(PersistableBundle bundle) {
Rui Qiua86ab602021-07-02 10:40:36 -0700467 try {
Rui Qiuaba02082021-08-16 16:24:00 -0700468 mListener.onSuccess(bundle);
Rui Qiua86ab602021-07-02 10:40:36 -0700469 } catch (RemoteException e) {
470 // nothing to do
471 }
472 }
473
Rui Qiuea6a4942021-09-07 15:05:34 -0700474 /** Mocks script producing final result. */
475 public void notifyScriptFinish(PersistableBundle bundle) {
476 try {
477 mListener.onScriptFinished(bundle);
478 } catch (RemoteException e) {
479 // nothing to do
480 }
481 }
482
Rui Qiu862d6ea2021-08-17 15:00:09 -0700483 /** Mocks script finished with error. */
484 public void notifyScriptError(int errorType, String errorMessage) {
485 try {
486 mListener.onError(errorType, errorMessage, null);
487 } catch (RemoteException e) {
488 // nothing to do
489 }
490 }
491
Rui Qiua86ab602021-07-02 10:40:36 -0700492 /** Fails the next N invokeScript() call. */
493 public void failNextApiCalls(int n) {
494 mFailApi = n;
495 }
496
497 /** Returns number of times the ScriptExecutor API was invoked. */
498 public int getApiInvocationCount() {
499 return mApiInvocationCount;
500 }
Rui Qiuea6a4942021-09-07 15:05:34 -0700501
502 /** Returns the interim data passed in invokeScript(). */
503 public PersistableBundle getSavedState() {
504 return mSavedState;
505 }
Rui Qiua86ab602021-07-02 10:40:36 -0700506 }
Rui Qiu337cb242021-06-03 11:26:16 -0700507}