blob: 15aa46129c570e44e36a31e4ca23af8003b04d16 [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;
Rui Qiuf4aa2e52021-09-27 16:30:00 -070030import android.car.AbstractExtendedMockitoCarServiceTestCase;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070031import android.car.hardware.CarPropertyConfig;
Rui Qiua86ab602021-07-02 10:40:36 -070032import android.content.Context;
33import android.content.ServiceConnection;
Rui Qiu033041e2021-06-21 14:59:11 -070034import android.os.Handler;
Rui Qiua86ab602021-07-02 10:40:36 -070035import android.os.IBinder;
Max Dashouk6987e9c2021-09-20 13:19:30 -070036import android.os.ParcelFileDescriptor;
Rui Qiuaba02082021-08-16 16:24:00 -070037import android.os.PersistableBundle;
Rui Qiua86ab602021-07-02 10:40:36 -070038import android.os.RemoteException;
Rui Qiu033041e2021-06-21 14:59:11 -070039import android.os.SystemClock;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070040
41import com.android.car.CarPropertyService;
Rui Qiuaba02082021-08-16 16:24:00 -070042import com.android.car.telemetry.ResultStore;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070043import com.android.car.telemetry.TelemetryProto;
44import com.android.car.telemetry.publisher.PublisherFactory;
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -070045import com.android.car.telemetry.publisher.StatsManagerProxy;
Max Dashouk9912ac32021-08-13 17:54:51 +000046import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutor;
47import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutorListener;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070048
49import org.junit.Before;
Rui Qiu337cb242021-06-03 11:26:16 -070050import org.junit.Test;
51import org.junit.runner.RunWith;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070052import org.mockito.Mock;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070053import org.mockito.Mockito;
Rui Qiuaf750102021-09-17 13:27:23 -070054import org.mockito.invocation.InvocationOnMock;
Rui Qiu337cb242021-06-03 11:26:16 -070055import org.mockito.junit.MockitoJUnitRunner;
Rui Qiuaf750102021-09-17 13:27:23 -070056import org.mockito.stubbing.Answer;
Rui Qiu337cb242021-06-03 11:26:16 -070057
Rui Qiuf4aa2e52021-09-27 16:30:00 -070058import java.io.IOException;
59import java.io.InputStream;
Rui Qiuda9f0ba2021-09-14 12:15:37 -070060import java.nio.file.Files;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070061import java.util.Collections;
Rui Qiuf4aa2e52021-09-27 16:30:00 -070062import java.util.Objects;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070063import java.util.concurrent.CountDownLatch;
Rui Qiu033041e2021-06-21 14:59:11 -070064import java.util.concurrent.PriorityBlockingQueue;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070065import java.util.concurrent.TimeUnit;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070066
Rui Qiu337cb242021-06-03 11:26:16 -070067@RunWith(MockitoJUnitRunner.class)
Rui Qiuf4aa2e52021-09-27 16:30:00 -070068public class DataBrokerTest extends AbstractExtendedMockitoCarServiceTestCase {
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070069 private static final int PROP_ID = 100;
70 private static final int PROP_AREA = 200;
Rui Qiu033041e2021-06-21 14:59:11 -070071 private static final int PRIORITY_HIGH = 1;
Rui Qiud95f95d2021-08-17 12:29:33 -070072 private static final int PRIORITY_LOW = 100;
Rui Qiu033041e2021-06-21 14:59:11 -070073 private static final long TIMEOUT_MS = 5_000L;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070074 private static final CarPropertyConfig<Integer> PROP_CONFIG =
75 CarPropertyConfig.newBuilder(Integer.class, PROP_ID, PROP_AREA).setAccess(
76 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ).build();
Rui Qiu337cb242021-06-03 11:26:16 -070077 private static final TelemetryProto.VehiclePropertyPublisher
78 VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION =
79 TelemetryProto.VehiclePropertyPublisher.newBuilder().setReadRate(
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070080 1).setVehiclePropertyId(PROP_ID).build();
Rui Qiu337cb242021-06-03 11:26:16 -070081 private static final TelemetryProto.Publisher PUBLISHER_CONFIGURATION =
82 TelemetryProto.Publisher.newBuilder().setVehicleProperty(
83 VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION).build();
84 private static final TelemetryProto.Subscriber SUBSCRIBER_FOO =
85 TelemetryProto.Subscriber.newBuilder().setHandler("function_name_foo").setPublisher(
Rui Qiud95f95d2021-08-17 12:29:33 -070086 PUBLISHER_CONFIGURATION).setPriority(PRIORITY_HIGH).build();
Rui Qiu337cb242021-06-03 11:26:16 -070087 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_FOO =
88 TelemetryProto.MetricsConfig.newBuilder().setName("Foo").setVersion(
89 1).addSubscribers(SUBSCRIBER_FOO).build();
90 private static final TelemetryProto.Subscriber SUBSCRIBER_BAR =
91 TelemetryProto.Subscriber.newBuilder().setHandler("function_name_bar").setPublisher(
Rui Qiud95f95d2021-08-17 12:29:33 -070092 PUBLISHER_CONFIGURATION).setPriority(PRIORITY_LOW).build();
Rui Qiu337cb242021-06-03 11:26:16 -070093 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_BAR =
94 TelemetryProto.MetricsConfig.newBuilder().setName("Bar").setVersion(
95 1).addSubscribers(SUBSCRIBER_BAR).build();
96
Rui Qiuea6a4942021-09-07 15:05:34 -070097
98 // when count reaches 0, all handler messages are scheduled to be dispatched after current time
Rui Qiuac8b5ba2021-08-25 16:04:17 -070099 private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700100 private PersistableBundle mData = new PersistableBundle();
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700101 private DataBrokerImpl mDataBroker;
Rui Qiua86ab602021-07-02 10:40:36 -0700102 private FakeScriptExecutor mFakeScriptExecutor;
Rui Qiu033041e2021-06-21 14:59:11 -0700103 private ScriptExecutionTask mHighPriorityTask;
104 private ScriptExecutionTask mLowPriorityTask;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700105
Rui Qiua86ab602021-07-02 10:40:36 -0700106 @Mock
107 private Context mMockContext;
108 @Mock
109 private CarPropertyService mMockCarPropertyService;
110 @Mock
Rui Qiuaa499ce2021-09-29 15:47:06 -0700111 private DataBroker.ScriptFinishedCallback mMockScriptFinishedCallback;
112 @Mock
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700113 private Handler mMockHandler;
114 @Mock
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700115 private StatsManagerProxy mMockStatsManager;
116 @Mock
Rui Qiua86ab602021-07-02 10:40:36 -0700117 private IBinder mMockScriptExecutorBinder;
Rui Qiuaba02082021-08-16 16:24:00 -0700118 @Mock
119 private ResultStore mMockResultStore;
Rui Qiua86ab602021-07-02 10:40:36 -0700120
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700121 @Before
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700122 public void setUp() throws Exception {
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700123 when(mMockCarPropertyService.getPropertyList())
124 .thenReturn(Collections.singletonList(PROP_CONFIG));
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700125 PublisherFactory factory = new PublisherFactory(
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700126 mMockCarPropertyService, mMockHandler, mMockStatsManager,
127 Files.createTempDirectory("telemetry_test").toFile());
Rui Qiuaba02082021-08-16 16:24:00 -0700128 mDataBroker = new DataBrokerImpl(mMockContext, factory, mMockResultStore);
Rui Qiuaa499ce2021-09-29 15:47:06 -0700129 mDataBroker.setOnScriptFinishedCallback(mMockScriptFinishedCallback);
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700130 // add IdleHandler to get notified when all messages and posts are handled
131 mDataBroker.getTelemetryHandler().getLooper().getQueue().addIdleHandler(() -> {
132 mIdleHandlerLatch.countDown();
133 return true;
134 });
Rui Qiua86ab602021-07-02 10:40:36 -0700135
136 mFakeScriptExecutor = new FakeScriptExecutor();
137 when(mMockScriptExecutorBinder.queryLocalInterface(anyString()))
138 .thenReturn(mFakeScriptExecutor);
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700139 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(i -> {
140 ServiceConnection conn = i.getArgument(1);
141 conn.onServiceConnected(null, mMockScriptExecutorBinder);
142 return true;
143 });
Rui Qiua86ab602021-07-02 10:40:36 -0700144
Rui Qiu033041e2021-06-21 14:59:11 -0700145 mHighPriorityTask = new ScriptExecutionTask(
Rui Qiud95f95d2021-08-17 12:29:33 -0700146 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
Rui Qiuea6a4942021-09-07 15:05:34 -0700147 mData,
Rui Qiu033041e2021-06-21 14:59:11 -0700148 SystemClock.elapsedRealtime());
149 mLowPriorityTask = new ScriptExecutionTask(
Rui Qiud95f95d2021-08-17 12:29:33 -0700150 new DataSubscriber(mDataBroker, METRICS_CONFIG_BAR, SUBSCRIBER_BAR),
Rui Qiuea6a4942021-09-07 15:05:34 -0700151 mData,
Rui Qiu033041e2021-06-21 14:59:11 -0700152 SystemClock.elapsedRealtime());
153 }
154
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700155 @Override
156 protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
157 builder.spyStatic(ParcelFileDescriptor.class);
158 }
159
Rui Qiu033041e2021-06-21 14:59:11 -0700160 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700161 public void testSetTaskExecutionPriority_whenNoTask_shouldNotInvokeScriptExecutor()
162 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700163 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
164
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700165 waitForTelemetryThreadToFinish();
166 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700167 }
168
169 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700170 public void testSetTaskExecutionPriority_whenNextTaskPriorityLow_shouldNotRunTask()
171 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700172 mDataBroker.getTaskQueue().add(mLowPriorityTask);
173
174 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
175
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700176 waitForTelemetryThreadToFinish();
Rui Qiu033041e2021-06-21 14:59:11 -0700177 // task is not polled
178 assertThat(mDataBroker.getTaskQueue().peek()).isEqualTo(mLowPriorityTask);
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700179 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700180 }
181
182 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700183 public void testSetTaskExecutionPriority_whenNextTaskPriorityHigh_shouldInvokeScriptExecutor()
184 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700185 mDataBroker.getTaskQueue().add(mHighPriorityTask);
186
187 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
188
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700189 waitForTelemetryThreadToFinish();
Rui Qiu033041e2021-06-21 14:59:11 -0700190 // task is polled and run
191 assertThat(mDataBroker.getTaskQueue().peek()).isNull();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700192 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiu033041e2021-06-21 14:59:11 -0700193 }
194
195 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700196 public void testScheduleNextTask_whenNoTask_shouldNotInvokeScriptExecutor() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700197 mDataBroker.scheduleNextTask();
198
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700199 waitForTelemetryThreadToFinish();
200 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700201 }
202
203 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700204 public void testScheduleNextTask_whenTaskInProgress_shouldNotInvokeScriptExecutorAgain()
205 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700206 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
207 taskQueue.add(mHighPriorityTask);
208 mDataBroker.scheduleNextTask(); // start a task
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700209 waitForTelemetryThreadToFinish();
Rui Qiu033041e2021-06-21 14:59:11 -0700210 assertThat(taskQueue.peek()).isNull(); // assert that task is polled and running
211 taskQueue.add(mHighPriorityTask); // add another task into the queue
212
213 mDataBroker.scheduleNextTask(); // schedule next task while the last task is in progress
214
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700215 waitForTelemetryThreadToFinish();
Rui Qiua86ab602021-07-02 10:40:36 -0700216 // verify task is not polled
217 assertThat(taskQueue.peek()).isEqualTo(mHighPriorityTask);
218 // expect one invocation for the task that is running
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700219 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiua86ab602021-07-02 10:40:36 -0700220 }
221
222 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700223 public void testScheduleNextTask_whenTaskCompletes_shouldAutomaticallyScheduleNextTask()
224 throws Exception {
Rui Qiua86ab602021-07-02 10:40:36 -0700225 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
226 // add two tasks into the queue for execution
227 taskQueue.add(mHighPriorityTask);
228 taskQueue.add(mHighPriorityTask);
229
230 mDataBroker.scheduleNextTask(); // start a task
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700231 waitForTelemetryThreadToFinish();
Rui Qiua86ab602021-07-02 10:40:36 -0700232 // end a task, should automatically schedule the next task
Rui Qiuea6a4942021-09-07 15:05:34 -0700233 mFakeScriptExecutor.notifyScriptSuccess(mData); // posts to telemetry handler
Rui Qiua86ab602021-07-02 10:40:36 -0700234
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700235 waitForTelemetryThreadToFinish();
Rui Qiua86ab602021-07-02 10:40:36 -0700236 // verify queue is empty, both tasks are polled and executed
237 assertThat(taskQueue.peek()).isNull();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700238 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(2);
Rui Qiua86ab602021-07-02 10:40:36 -0700239 }
240
241 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700242 public void testScheduleNextTask_onScriptSuccess_shouldStoreInterimResult() throws Exception {
Rui Qiuea6a4942021-09-07 15:05:34 -0700243 mData.putBoolean("script is finished", false);
244 mData.putDouble("value of euler's number", 2.71828);
Rui Qiuaba02082021-08-16 16:24:00 -0700245 mDataBroker.getTaskQueue().add(mHighPriorityTask);
Rui Qiuaba02082021-08-16 16:24:00 -0700246
247 mDataBroker.scheduleNextTask();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700248 waitForTelemetryThreadToFinish();
Rui Qiuea6a4942021-09-07 15:05:34 -0700249 mFakeScriptExecutor.notifyScriptSuccess(mData); // posts to telemetry handler
Rui Qiuaba02082021-08-16 16:24:00 -0700250
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700251 waitForTelemetryThreadToFinish();
252 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700253 verify(mMockResultStore).putInterimResult(
254 eq(mHighPriorityTask.getMetricsConfig().getName()), eq(mData));
255 }
256
257 @Test
Rui Qiu862d6ea2021-08-17 15:00:09 -0700258 public void testScheduleNextTask_onScriptError_shouldStoreErrorObject() throws Exception {
259 mDataBroker.getTaskQueue().add(mHighPriorityTask);
260 TelemetryProto.TelemetryError.ErrorType errorType =
261 TelemetryProto.TelemetryError.ErrorType.LUA_RUNTIME_ERROR;
262 String errorMessage = "test onError";
263 TelemetryProto.TelemetryError expectedError = TelemetryProto.TelemetryError.newBuilder()
264 .setErrorType(errorType)
265 .setMessage(errorMessage)
266 .build();
267
268 mDataBroker.scheduleNextTask();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700269 waitForTelemetryThreadToFinish();
Rui Qiu862d6ea2021-08-17 15:00:09 -0700270 mFakeScriptExecutor.notifyScriptError(errorType.getNumber(), errorMessage);
271
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700272 waitForTelemetryThreadToFinish();
273 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiu862d6ea2021-08-17 15:00:09 -0700274 verify(mMockResultStore).putError(eq(METRICS_CONFIG_FOO.getName()), eq(expectedError));
275 }
276
277 @Test
Rui Qiuea6a4942021-09-07 15:05:34 -0700278 public void testScheduleNextTask_whenScriptFinishes_shouldStoreFinalResult()
279 throws Exception {
280 mData.putBoolean("script is finished", true);
281 mData.putDouble("value of pi", 3.14159265359);
282 mDataBroker.getTaskQueue().add(mHighPriorityTask);
283
284 mDataBroker.scheduleNextTask();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700285 waitForTelemetryThreadToFinish();
Rui Qiuea6a4942021-09-07 15:05:34 -0700286 mFakeScriptExecutor.notifyScriptFinish(mData); // posts to telemetry handler
287
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700288 waitForTelemetryThreadToFinish();
289 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700290 verify(mMockResultStore).putFinalResult(
291 eq(mHighPriorityTask.getMetricsConfig().getName()), eq(mData));
Rui Qiuaa499ce2021-09-29 15:47:06 -0700292 verify(mMockScriptFinishedCallback).onScriptFinished(eq(METRICS_CONFIG_FOO.getName()));
Rui Qiuea6a4942021-09-07 15:05:34 -0700293 }
294
295 @Test
296 public void testScheduleNextTask_whenInterimDataExists_shouldPassToScriptExecutor()
297 throws Exception {
298 mData.putDouble("value of golden ratio", 1.618033);
299 mDataBroker.getTaskQueue().add(mHighPriorityTask);
300 when(mMockResultStore.getInterimResult(mHighPriorityTask.getMetricsConfig().getName()))
301 .thenReturn(mData);
302
303 mDataBroker.scheduleNextTask();
304
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700305 waitForTelemetryThreadToFinish();
306 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700307 assertThat(mFakeScriptExecutor.getSavedState()).isEqualTo(mData);
Rui Qiuaba02082021-08-16 16:24:00 -0700308 }
309
310 @Test
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700311 public void testScheduleNextTask_largeInput_shouldPipeData() throws Exception {
312 mData.putBooleanArray("1 MB Array", new boolean [1024 * 1024]);
313 mDataBroker.getTaskQueue().add(mHighPriorityTask);
314
315 mDataBroker.scheduleNextTask();
316
317 waitForTelemetryThreadToFinish();
318 assertThat(mFakeScriptExecutor.getInvokeScriptForLargeInputCount()).isEqualTo(1);
319 }
320
321 @Test
322 public void testScheduleNextTask_largeInputPipeIOException_shouldIgnoreCurrentTask()
323 throws Exception {
324 mData.putBooleanArray("1 MB Array", new boolean [1024 * 1024]);
325 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
326 taskQueue.add(mHighPriorityTask); // invokeScriptForLargeInput() path
327 taskQueue.add(new ScriptExecutionTask(
328 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
329 new PersistableBundle(),
330 SystemClock.elapsedRealtime())); // invokeScript() path
331 ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
332 when(ParcelFileDescriptor.createPipe()).thenReturn(fds);
333 fds[1].close(); // cause IO Exception in invokeScriptForLargeInput() path
334
335 mDataBroker.scheduleNextTask();
336
337 waitForTelemetryThreadToFinish();
338 assertThat(mFakeScriptExecutor.getInvokeScriptForLargeInputCount()).isEqualTo(1);
339 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
340 assertThat(taskQueue).isEmpty();
341 }
342
343 @Test
Rui Qiuaf750102021-09-17 13:27:23 -0700344 public void testScheduleNextTask_bindScriptExecutorFailedOnce_shouldRebind()
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700345 throws Exception {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700346 Mockito.reset(mMockContext);
Rui Qiuaf750102021-09-17 13:27:23 -0700347 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(
348 new Answer() {
349 private int mCount = 0;
350
351 @Override
352 public Object answer(InvocationOnMock invocation) {
353 if (mCount++ == 1) {
354 return false; // fail first attempt
355 }
356 ServiceConnection conn = invocation.getArgument(1);
357 conn.onServiceConnected(null, mMockScriptExecutorBinder);
358 return true; // second attempt should succeed
359 }
360 });
361 mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
Rui Qiua86ab602021-07-02 10:40:36 -0700362 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
363 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
364 taskQueue.add(mHighPriorityTask);
Rui Qiua86ab602021-07-02 10:40:36 -0700365
366 // will rebind to ScriptExecutor if it is null
367 mDataBroker.scheduleNextTask();
368
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700369 waitForTelemetryThreadToFinish();
Rui Qiuaf750102021-09-17 13:27:23 -0700370 assertThat(taskQueue.peek()).isNull();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700371 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuaf750102021-09-17 13:27:23 -0700372 }
373
374 @Test
375 public void testScheduleNextTask_bindScriptExecutorFailedMultipleTimes_shouldDisableBroker()
376 throws Exception {
377 // fail 6 future attempts to bind to it
378 Mockito.reset(mMockContext);
379 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any()))
380 .thenReturn(false, false, false, false, false, false);
381 mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
382 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
383 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
384 taskQueue.add(mHighPriorityTask);
385
386 // will rebind to ScriptExecutor if it is null
387 mDataBroker.scheduleNextTask();
388
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700389 waitForTelemetryThreadToFinish();
Rui Qiuaf750102021-09-17 13:27:23 -0700390 // broker disabled, all subscribers should have been removed
Rui Qiua86ab602021-07-02 10:40:36 -0700391 assertThat(mDataBroker.getSubscriptionMap()).hasSize(0);
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700392 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiua86ab602021-07-02 10:40:36 -0700393 }
394
395 @Test
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700396 public void testScheduleNextTask_whenScriptExecutorThrowsException_shouldResetAndTryAgain()
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700397 throws Exception {
Rui Qiua86ab602021-07-02 10:40:36 -0700398 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
399 taskQueue.add(mHighPriorityTask);
400 mFakeScriptExecutor.failNextApiCalls(1); // fail the next invokeScript() call
401
402 mDataBroker.scheduleNextTask();
403
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700404 waitForTelemetryThreadToFinish();
405 // invokeScript() failed, task is re-queued and re-run
406 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(2);
407 assertThat(taskQueue).isEmpty();
Rui Qiu033041e2021-06-21 14:59:11 -0700408 }
409
410 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700411 public void testAddTaskToQueue_shouldInvokeScriptExecutor() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700412 mDataBroker.addTaskToQueue(mHighPriorityTask);
413
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700414 waitForTelemetryThreadToFinish();
415 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700416 }
417
Rui Qiu337cb242021-06-03 11:26:16 -0700418 @Test
419 public void testAddMetricsConfiguration_newMetricsConfig() {
Rui Qiuf14c6a22021-07-14 10:39:16 -0700420 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
421
Rui Qiuf14c6a22021-07-14 10:39:16 -0700422 assertThat(mDataBroker.getSubscriptionMap()).hasSize(1);
423 assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_BAR.getName());
424 // there should be one data subscriber in the subscription list of METRICS_CONFIG_BAR
425 assertThat(mDataBroker.getSubscriptionMap().get(METRICS_CONFIG_BAR.getName())).hasSize(1);
426 }
427
428
429 @Test
430 public void testAddMetricsConfiguration_duplicateMetricsConfig_shouldDoNothing() {
431 // this metrics config has already been added in setUp()
Rui Qiu337cb242021-06-03 11:26:16 -0700432 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
433
Rui Qiuf14c6a22021-07-14 10:39:16 -0700434 assertThat(mDataBroker.getSubscriptionMap()).hasSize(1);
Rui Qiu337cb242021-06-03 11:26:16 -0700435 assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_FOO.getName());
Rui Qiu337cb242021-06-03 11:26:16 -0700436 assertThat(mDataBroker.getSubscriptionMap().get(METRICS_CONFIG_FOO.getName())).hasSize(1);
437 }
438
439 @Test
Rui Qiuf14c6a22021-07-14 10:39:16 -0700440 public void testRemoveMetricsConfiguration_shouldRemoveAllAssociatedTasks() {
Rui Qiu337cb242021-06-03 11:26:16 -0700441 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
442 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
Rui Qiud95f95d2021-08-17 12:29:33 -0700443 ScriptExecutionTask taskWithMetricsConfigFoo = new ScriptExecutionTask(
444 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
Rui Qiuea6a4942021-09-07 15:05:34 -0700445 mData,
Rui Qiuf14c6a22021-07-14 10:39:16 -0700446 SystemClock.elapsedRealtime());
447 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
448 taskQueue.add(mHighPriorityTask); // associated with METRICS_CONFIG_FOO
Rui Qiud95f95d2021-08-17 12:29:33 -0700449 taskQueue.add(mLowPriorityTask); // associated with METRICS_CONFIG_BAR
450 taskQueue.add(taskWithMetricsConfigFoo); // associated with METRICS_CONFIG_FOO
Rui Qiuf14c6a22021-07-14 10:39:16 -0700451 assertThat(taskQueue).hasSize(3);
452
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700453 mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_FOO.getName());
Rui Qiu337cb242021-06-03 11:26:16 -0700454
Rui Qiuf14c6a22021-07-14 10:39:16 -0700455 assertThat(taskQueue).hasSize(1);
Rui Qiud95f95d2021-08-17 12:29:33 -0700456 assertThat(taskQueue.poll()).isEqualTo(mLowPriorityTask);
Rui Qiuf14c6a22021-07-14 10:39:16 -0700457 }
458
459 @Test
460 public void testRemoveMetricsConfiguration_whenMetricsConfigNonExistent_shouldDoNothing() {
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700461 mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_BAR.getName());
Rui Qiuf14c6a22021-07-14 10:39:16 -0700462
Rui Qiuf14c6a22021-07-14 10:39:16 -0700463 assertThat(mDataBroker.getSubscriptionMap()).hasSize(0);
Rui Qiu337cb242021-06-03 11:26:16 -0700464 }
465
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700466 @Test
467 public void testRemoveAllMetricsConfigurations_shouldRemoveTasksAndClearSubscriptionMap() {
468 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
469 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
470 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
471 taskQueue.add(mHighPriorityTask); // associated with METRICS_CONFIG_FOO
472 taskQueue.add(mLowPriorityTask); // associated with METRICS_CONFIG_BAR
473
474 mDataBroker.removeAllMetricsConfigurations();
475
476 assertThat(taskQueue).isEmpty();
477 assertThat(mDataBroker.getSubscriptionMap()).isEmpty();
478 }
479
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700480 private void waitForTelemetryThreadToFinish() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700481 assertWithMessage("handler not idle in %sms", TIMEOUT_MS)
Rui Qiua4a8fd02021-09-02 19:08:40 -0700482 .that(mIdleHandlerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
Rui Qiuea6a4942021-09-07 15:05:34 -0700483 mIdleHandlerLatch = new CountDownLatch(1); // reset idle handler condition
Rui Qiu337cb242021-06-03 11:26:16 -0700484 }
Rui Qiua86ab602021-07-02 10:40:36 -0700485
486 private static class FakeScriptExecutor implements IScriptExecutor {
487 private IScriptExecutorListener mListener;
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700488 private int mInvokeScriptCount = 0;
489 private int mInvokeScriptForLargeInputCount = 0;
Rui Qiua86ab602021-07-02 10:40:36 -0700490 private int mFailApi = 0;
Rui Qiuea6a4942021-09-07 15:05:34 -0700491 private PersistableBundle mSavedState = null;
Rui Qiua86ab602021-07-02 10:40:36 -0700492
493 @Override
Max Dashoukdafdd222021-08-20 10:42:25 -0700494 public void invokeScript(String scriptBody, String functionName,
495 PersistableBundle publishedData, @Nullable PersistableBundle savedState,
496 IScriptExecutorListener listener)
Rui Qiua86ab602021-07-02 10:40:36 -0700497 throws RemoteException {
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700498 mInvokeScriptCount++;
Rui Qiuea6a4942021-09-07 15:05:34 -0700499 mSavedState = savedState;
Rui Qiua86ab602021-07-02 10:40:36 -0700500 mListener = listener;
501 if (mFailApi > 0) {
502 mFailApi--;
503 throw new RemoteException("Simulated failure");
504 }
505 }
506
507 @Override
Max Dashouk6987e9c2021-09-20 13:19:30 -0700508 public void invokeScriptForLargeInput(String scriptBody, String functionName,
509 ParcelFileDescriptor publishedDataFileDescriptor,
510 @Nullable PersistableBundle savedState,
511 IScriptExecutorListener listener) throws RemoteException {
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700512 mInvokeScriptForLargeInputCount++;
Max Dashouk6987e9c2021-09-20 13:19:30 -0700513 mSavedState = savedState;
514 mListener = listener;
515 if (mFailApi > 0) {
516 mFailApi--;
517 throw new RemoteException("Simulated failure");
518 }
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700519 // Since DataBrokerImpl and FakeScriptExecutor are in the same process, they do not
520 // use real IPC and share the fd. When DataBroker closes the fd, it affects
521 // FakeScriptExecutor. Therefore FakeScriptExecutor must dup the fd before it is
522 // closed by DataBroker
523 ParcelFileDescriptor dup = null;
524 try {
525 dup = publishedDataFileDescriptor.dup();
526 } catch (IOException e) { }
527 final ParcelFileDescriptor fd = Objects.requireNonNull(dup);
528 // to prevent deadlock, read and write must happen on separate threads
529 Handler.getMain().post(() -> {
530 try (InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
531 PersistableBundle.readFromStream(input);
532 } catch (IOException e) { }
533 });
Max Dashouk6987e9c2021-09-20 13:19:30 -0700534 }
535
536 @Override
Rui Qiua86ab602021-07-02 10:40:36 -0700537 public IBinder asBinder() {
538 return null;
539 }
540
Rui Qiuaba02082021-08-16 16:24:00 -0700541 /** Mocks script temporary completion. */
Max Dashoukdafdd222021-08-20 10:42:25 -0700542 public void notifyScriptSuccess(PersistableBundle bundle) {
Rui Qiua86ab602021-07-02 10:40:36 -0700543 try {
Rui Qiuaba02082021-08-16 16:24:00 -0700544 mListener.onSuccess(bundle);
Rui Qiua86ab602021-07-02 10:40:36 -0700545 } catch (RemoteException e) {
546 // nothing to do
547 }
548 }
549
Rui Qiuea6a4942021-09-07 15:05:34 -0700550 /** Mocks script producing final result. */
551 public void notifyScriptFinish(PersistableBundle bundle) {
552 try {
553 mListener.onScriptFinished(bundle);
554 } catch (RemoteException e) {
555 // nothing to do
556 }
557 }
558
Rui Qiu862d6ea2021-08-17 15:00:09 -0700559 /** Mocks script finished with error. */
560 public void notifyScriptError(int errorType, String errorMessage) {
561 try {
562 mListener.onError(errorType, errorMessage, null);
563 } catch (RemoteException e) {
564 // nothing to do
565 }
566 }
567
Rui Qiua86ab602021-07-02 10:40:36 -0700568 /** Fails the next N invokeScript() call. */
569 public void failNextApiCalls(int n) {
570 mFailApi = n;
571 }
572
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700573 /** Returns number of times invokeScript() was called. */
574 public int getInvokeScriptCount() {
575 return mInvokeScriptCount;
576 }
577
578 /** Returns number of times invokeScriptForLargeInput() was called. */
579 public int getInvokeScriptForLargeInputCount() {
580 return mInvokeScriptForLargeInputCount;
Rui Qiua86ab602021-07-02 10:40:36 -0700581 }
Rui Qiuea6a4942021-09-07 15:05:34 -0700582
583 /** Returns the interim data passed in invokeScript(). */
584 public PersistableBundle getSavedState() {
585 return mSavedState;
586 }
Rui Qiua86ab602021-07-02 10:40:36 -0700587 }
Rui Qiu337cb242021-06-03 11:26:16 -0700588}