blob: 6983c45a616f44808e8c135588dd09dba168588e [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 Qiu36a12072021-10-14 13:26:20 -070050import org.junit.Ignore;
Rui Qiu337cb242021-06-03 11:26:16 -070051import org.junit.Test;
52import org.junit.runner.RunWith;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070053import org.mockito.Mock;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070054import org.mockito.Mockito;
Rui Qiuaf750102021-09-17 13:27:23 -070055import org.mockito.invocation.InvocationOnMock;
Rui Qiu337cb242021-06-03 11:26:16 -070056import org.mockito.junit.MockitoJUnitRunner;
Rui Qiuaf750102021-09-17 13:27:23 -070057import org.mockito.stubbing.Answer;
Rui Qiu337cb242021-06-03 11:26:16 -070058
Rui Qiuf4aa2e52021-09-27 16:30:00 -070059import java.io.IOException;
60import java.io.InputStream;
Rui Qiuda9f0ba2021-09-14 12:15:37 -070061import java.nio.file.Files;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070062import java.util.Collections;
Rui Qiuf4aa2e52021-09-27 16:30:00 -070063import java.util.Objects;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070064import java.util.concurrent.CountDownLatch;
Rui Qiu033041e2021-06-21 14:59:11 -070065import java.util.concurrent.PriorityBlockingQueue;
Rui Qiuac8b5ba2021-08-25 16:04:17 -070066import java.util.concurrent.TimeUnit;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070067
Rui Qiu337cb242021-06-03 11:26:16 -070068@RunWith(MockitoJUnitRunner.class)
Rui Qiuf4aa2e52021-09-27 16:30:00 -070069public class DataBrokerTest extends AbstractExtendedMockitoCarServiceTestCase {
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070070 private static final int PROP_ID = 100;
71 private static final int PROP_AREA = 200;
Rui Qiu033041e2021-06-21 14:59:11 -070072 private static final int PRIORITY_HIGH = 1;
Rui Qiud95f95d2021-08-17 12:29:33 -070073 private static final int PRIORITY_LOW = 100;
Rui Qiu033041e2021-06-21 14:59:11 -070074 private static final long TIMEOUT_MS = 5_000L;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070075 private static final CarPropertyConfig<Integer> PROP_CONFIG =
76 CarPropertyConfig.newBuilder(Integer.class, PROP_ID, PROP_AREA).setAccess(
77 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ).build();
Rui Qiu337cb242021-06-03 11:26:16 -070078 private static final TelemetryProto.VehiclePropertyPublisher
79 VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION =
80 TelemetryProto.VehiclePropertyPublisher.newBuilder().setReadRate(
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -070081 1).setVehiclePropertyId(PROP_ID).build();
Rui Qiu337cb242021-06-03 11:26:16 -070082 private static final TelemetryProto.Publisher PUBLISHER_CONFIGURATION =
83 TelemetryProto.Publisher.newBuilder().setVehicleProperty(
84 VEHICLE_PROPERTY_PUBLISHER_CONFIGURATION).build();
85 private static final TelemetryProto.Subscriber SUBSCRIBER_FOO =
86 TelemetryProto.Subscriber.newBuilder().setHandler("function_name_foo").setPublisher(
Rui Qiud95f95d2021-08-17 12:29:33 -070087 PUBLISHER_CONFIGURATION).setPriority(PRIORITY_HIGH).build();
Rui Qiu337cb242021-06-03 11:26:16 -070088 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_FOO =
89 TelemetryProto.MetricsConfig.newBuilder().setName("Foo").setVersion(
90 1).addSubscribers(SUBSCRIBER_FOO).build();
91 private static final TelemetryProto.Subscriber SUBSCRIBER_BAR =
92 TelemetryProto.Subscriber.newBuilder().setHandler("function_name_bar").setPublisher(
Rui Qiud95f95d2021-08-17 12:29:33 -070093 PUBLISHER_CONFIGURATION).setPriority(PRIORITY_LOW).build();
Rui Qiu337cb242021-06-03 11:26:16 -070094 private static final TelemetryProto.MetricsConfig METRICS_CONFIG_BAR =
95 TelemetryProto.MetricsConfig.newBuilder().setName("Bar").setVersion(
96 1).addSubscribers(SUBSCRIBER_BAR).build();
97
Rui Qiuea6a4942021-09-07 15:05:34 -070098
99 // when count reaches 0, all handler messages are scheduled to be dispatched after current time
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700100 private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700101 private PersistableBundle mData = new PersistableBundle();
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700102 private DataBrokerImpl mDataBroker;
Rui Qiua86ab602021-07-02 10:40:36 -0700103 private FakeScriptExecutor mFakeScriptExecutor;
Rui Qiu033041e2021-06-21 14:59:11 -0700104 private ScriptExecutionTask mHighPriorityTask;
105 private ScriptExecutionTask mLowPriorityTask;
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700106
Rui Qiua86ab602021-07-02 10:40:36 -0700107 @Mock
108 private Context mMockContext;
109 @Mock
110 private CarPropertyService mMockCarPropertyService;
111 @Mock
Rui Qiuaa499ce2021-09-29 15:47:06 -0700112 private DataBroker.ScriptFinishedCallback mMockScriptFinishedCallback;
113 @Mock
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700114 private Handler mMockHandler;
115 @Mock
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700116 private StatsManagerProxy mMockStatsManager;
117 @Mock
Rui Qiua86ab602021-07-02 10:40:36 -0700118 private IBinder mMockScriptExecutorBinder;
Rui Qiuaba02082021-08-16 16:24:00 -0700119 @Mock
120 private ResultStore mMockResultStore;
Rui Qiua86ab602021-07-02 10:40:36 -0700121
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700122 @Before
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700123 public void setUp() throws Exception {
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700124 when(mMockCarPropertyService.getPropertyList())
125 .thenReturn(Collections.singletonList(PROP_CONFIG));
Zhomart Mukhamejanovcdcb4e12021-08-23 18:24:40 -0700126 PublisherFactory factory = new PublisherFactory(
Rui Qiuda9f0ba2021-09-14 12:15:37 -0700127 mMockCarPropertyService, mMockHandler, mMockStatsManager,
128 Files.createTempDirectory("telemetry_test").toFile());
Rui Qiuaba02082021-08-16 16:24:00 -0700129 mDataBroker = new DataBrokerImpl(mMockContext, factory, mMockResultStore);
Rui Qiuaa499ce2021-09-29 15:47:06 -0700130 mDataBroker.setOnScriptFinishedCallback(mMockScriptFinishedCallback);
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700131 // add IdleHandler to get notified when all messages and posts are handled
132 mDataBroker.getTelemetryHandler().getLooper().getQueue().addIdleHandler(() -> {
133 mIdleHandlerLatch.countDown();
134 return true;
135 });
Rui Qiua86ab602021-07-02 10:40:36 -0700136
137 mFakeScriptExecutor = new FakeScriptExecutor();
138 when(mMockScriptExecutorBinder.queryLocalInterface(anyString()))
139 .thenReturn(mFakeScriptExecutor);
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700140 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(i -> {
141 ServiceConnection conn = i.getArgument(1);
142 conn.onServiceConnected(null, mMockScriptExecutorBinder);
143 return true;
144 });
Rui Qiua86ab602021-07-02 10:40:36 -0700145
Rui Qiu033041e2021-06-21 14:59:11 -0700146 mHighPriorityTask = new ScriptExecutionTask(
Rui Qiud95f95d2021-08-17 12:29:33 -0700147 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
Rui Qiuea6a4942021-09-07 15:05:34 -0700148 mData,
Rui Qiu033041e2021-06-21 14:59:11 -0700149 SystemClock.elapsedRealtime());
150 mLowPriorityTask = new ScriptExecutionTask(
Rui Qiud95f95d2021-08-17 12:29:33 -0700151 new DataSubscriber(mDataBroker, METRICS_CONFIG_BAR, SUBSCRIBER_BAR),
Rui Qiuea6a4942021-09-07 15:05:34 -0700152 mData,
Rui Qiu033041e2021-06-21 14:59:11 -0700153 SystemClock.elapsedRealtime());
154 }
155
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700156 @Override
157 protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
158 builder.spyStatic(ParcelFileDescriptor.class);
159 }
160
Rui Qiu033041e2021-06-21 14:59:11 -0700161 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700162 public void testSetTaskExecutionPriority_whenNoTask_shouldNotInvokeScriptExecutor()
163 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700164 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
165
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700166 waitForTelemetryThreadToFinish();
167 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700168 }
169
170 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700171 public void testSetTaskExecutionPriority_whenNextTaskPriorityLow_shouldNotRunTask()
172 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700173 mDataBroker.getTaskQueue().add(mLowPriorityTask);
174
175 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
176
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700177 waitForTelemetryThreadToFinish();
Rui Qiu033041e2021-06-21 14:59:11 -0700178 // task is not polled
179 assertThat(mDataBroker.getTaskQueue().peek()).isEqualTo(mLowPriorityTask);
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700180 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700181 }
182
183 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700184 public void testSetTaskExecutionPriority_whenNextTaskPriorityHigh_shouldInvokeScriptExecutor()
185 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700186 mDataBroker.getTaskQueue().add(mHighPriorityTask);
187
188 mDataBroker.setTaskExecutionPriority(PRIORITY_HIGH);
189
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700190 waitForTelemetryThreadToFinish();
Rui Qiu033041e2021-06-21 14:59:11 -0700191 // task is polled and run
192 assertThat(mDataBroker.getTaskQueue().peek()).isNull();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700193 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiu033041e2021-06-21 14:59:11 -0700194 }
195
196 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700197 public void testScheduleNextTask_whenNoTask_shouldNotInvokeScriptExecutor() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700198 mDataBroker.scheduleNextTask();
199
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700200 waitForTelemetryThreadToFinish();
201 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiu033041e2021-06-21 14:59:11 -0700202 }
203
204 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700205 public void testScheduleNextTask_whenTaskInProgress_shouldNotInvokeScriptExecutorAgain()
206 throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700207 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
208 taskQueue.add(mHighPriorityTask);
209 mDataBroker.scheduleNextTask(); // start a task
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700210 waitForTelemetryThreadToFinish();
Rui Qiu033041e2021-06-21 14:59:11 -0700211 assertThat(taskQueue.peek()).isNull(); // assert that task is polled and running
212 taskQueue.add(mHighPriorityTask); // add another task into the queue
213
214 mDataBroker.scheduleNextTask(); // schedule next task while the last task is in progress
215
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700216 waitForTelemetryThreadToFinish();
Rui Qiua86ab602021-07-02 10:40:36 -0700217 // verify task is not polled
218 assertThat(taskQueue.peek()).isEqualTo(mHighPriorityTask);
219 // expect one invocation for the task that is running
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700220 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiua86ab602021-07-02 10:40:36 -0700221 }
222
223 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700224 public void testScheduleNextTask_whenTaskCompletes_shouldAutomaticallyScheduleNextTask()
225 throws Exception {
Rui Qiua86ab602021-07-02 10:40:36 -0700226 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
227 // add two tasks into the queue for execution
228 taskQueue.add(mHighPriorityTask);
229 taskQueue.add(mHighPriorityTask);
230
231 mDataBroker.scheduleNextTask(); // start a task
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700232 waitForTelemetryThreadToFinish();
Rui Qiua86ab602021-07-02 10:40:36 -0700233 // end a task, should automatically schedule the next task
Rui Qiuea6a4942021-09-07 15:05:34 -0700234 mFakeScriptExecutor.notifyScriptSuccess(mData); // posts to telemetry handler
Rui Qiua86ab602021-07-02 10:40:36 -0700235
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700236 waitForTelemetryThreadToFinish();
Rui Qiua86ab602021-07-02 10:40:36 -0700237 // verify queue is empty, both tasks are polled and executed
238 assertThat(taskQueue.peek()).isNull();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700239 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(2);
Rui Qiua86ab602021-07-02 10:40:36 -0700240 }
241
242 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700243 public void testScheduleNextTask_onScriptSuccess_shouldStoreInterimResult() throws Exception {
Rui Qiuea6a4942021-09-07 15:05:34 -0700244 mData.putBoolean("script is finished", false);
245 mData.putDouble("value of euler's number", 2.71828);
Rui Qiuaba02082021-08-16 16:24:00 -0700246 mDataBroker.getTaskQueue().add(mHighPriorityTask);
Rui Qiuaba02082021-08-16 16:24:00 -0700247
248 mDataBroker.scheduleNextTask();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700249 waitForTelemetryThreadToFinish();
Rui Qiuea6a4942021-09-07 15:05:34 -0700250 mFakeScriptExecutor.notifyScriptSuccess(mData); // posts to telemetry handler
Rui Qiuaba02082021-08-16 16:24:00 -0700251
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700252 waitForTelemetryThreadToFinish();
253 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700254 verify(mMockResultStore).putInterimResult(
255 eq(mHighPriorityTask.getMetricsConfig().getName()), eq(mData));
256 }
257
258 @Test
Rui Qiu862d6ea2021-08-17 15:00:09 -0700259 public void testScheduleNextTask_onScriptError_shouldStoreErrorObject() throws Exception {
260 mDataBroker.getTaskQueue().add(mHighPriorityTask);
261 TelemetryProto.TelemetryError.ErrorType errorType =
262 TelemetryProto.TelemetryError.ErrorType.LUA_RUNTIME_ERROR;
263 String errorMessage = "test onError";
264 TelemetryProto.TelemetryError expectedError = TelemetryProto.TelemetryError.newBuilder()
265 .setErrorType(errorType)
266 .setMessage(errorMessage)
267 .build();
268
269 mDataBroker.scheduleNextTask();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700270 waitForTelemetryThreadToFinish();
Rui Qiu862d6ea2021-08-17 15:00:09 -0700271 mFakeScriptExecutor.notifyScriptError(errorType.getNumber(), errorMessage);
272
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700273 waitForTelemetryThreadToFinish();
274 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiu862d6ea2021-08-17 15:00:09 -0700275 verify(mMockResultStore).putError(eq(METRICS_CONFIG_FOO.getName()), eq(expectedError));
276 }
277
278 @Test
Rui Qiuea6a4942021-09-07 15:05:34 -0700279 public void testScheduleNextTask_whenScriptFinishes_shouldStoreFinalResult()
280 throws Exception {
281 mData.putBoolean("script is finished", true);
282 mData.putDouble("value of pi", 3.14159265359);
283 mDataBroker.getTaskQueue().add(mHighPriorityTask);
284
285 mDataBroker.scheduleNextTask();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700286 waitForTelemetryThreadToFinish();
Rui Qiuea6a4942021-09-07 15:05:34 -0700287 mFakeScriptExecutor.notifyScriptFinish(mData); // posts to telemetry handler
288
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700289 waitForTelemetryThreadToFinish();
290 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700291 verify(mMockResultStore).putFinalResult(
292 eq(mHighPriorityTask.getMetricsConfig().getName()), eq(mData));
Rui Qiuaa499ce2021-09-29 15:47:06 -0700293 verify(mMockScriptFinishedCallback).onScriptFinished(eq(METRICS_CONFIG_FOO.getName()));
Rui Qiuea6a4942021-09-07 15:05:34 -0700294 }
295
296 @Test
297 public void testScheduleNextTask_whenInterimDataExists_shouldPassToScriptExecutor()
298 throws Exception {
299 mData.putDouble("value of golden ratio", 1.618033);
300 mDataBroker.getTaskQueue().add(mHighPriorityTask);
301 when(mMockResultStore.getInterimResult(mHighPriorityTask.getMetricsConfig().getName()))
302 .thenReturn(mData);
303
304 mDataBroker.scheduleNextTask();
305
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700306 waitForTelemetryThreadToFinish();
307 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuea6a4942021-09-07 15:05:34 -0700308 assertThat(mFakeScriptExecutor.getSavedState()).isEqualTo(mData);
Rui Qiuaba02082021-08-16 16:24:00 -0700309 }
310
Rui Qiu36a12072021-10-14 13:26:20 -0700311 @Ignore("b/202869761: fix flake")
Rui Qiuaba02082021-08-16 16:24:00 -0700312 @Test
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700313 public void testScheduleNextTask_largeInput_shouldPipeData() throws Exception {
314 mData.putBooleanArray("1 MB Array", new boolean [1024 * 1024]);
315 mDataBroker.getTaskQueue().add(mHighPriorityTask);
316
317 mDataBroker.scheduleNextTask();
318
319 waitForTelemetryThreadToFinish();
320 assertThat(mFakeScriptExecutor.getInvokeScriptForLargeInputCount()).isEqualTo(1);
321 }
322
Rui Qiu36a12072021-10-14 13:26:20 -0700323 @Ignore("b/202869761: fix flake")
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700324 @Test
325 public void testScheduleNextTask_largeInputPipeIOException_shouldIgnoreCurrentTask()
326 throws Exception {
327 mData.putBooleanArray("1 MB Array", new boolean [1024 * 1024]);
328 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
329 taskQueue.add(mHighPriorityTask); // invokeScriptForLargeInput() path
330 taskQueue.add(new ScriptExecutionTask(
331 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
332 new PersistableBundle(),
333 SystemClock.elapsedRealtime())); // invokeScript() path
334 ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
335 when(ParcelFileDescriptor.createPipe()).thenReturn(fds);
336 fds[1].close(); // cause IO Exception in invokeScriptForLargeInput() path
337
338 mDataBroker.scheduleNextTask();
339
340 waitForTelemetryThreadToFinish();
341 assertThat(mFakeScriptExecutor.getInvokeScriptForLargeInputCount()).isEqualTo(1);
342 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
343 assertThat(taskQueue).isEmpty();
344 }
345
346 @Test
Rui Qiuaf750102021-09-17 13:27:23 -0700347 public void testScheduleNextTask_bindScriptExecutorFailedOnce_shouldRebind()
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700348 throws Exception {
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700349 Mockito.reset(mMockContext);
Rui Qiuaf750102021-09-17 13:27:23 -0700350 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(
351 new Answer() {
352 private int mCount = 0;
353
354 @Override
355 public Object answer(InvocationOnMock invocation) {
356 if (mCount++ == 1) {
357 return false; // fail first attempt
358 }
359 ServiceConnection conn = invocation.getArgument(1);
360 conn.onServiceConnected(null, mMockScriptExecutorBinder);
361 return true; // second attempt should succeed
362 }
363 });
364 mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
Rui Qiua86ab602021-07-02 10:40:36 -0700365 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
366 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
367 taskQueue.add(mHighPriorityTask);
Rui Qiua86ab602021-07-02 10:40:36 -0700368
369 // will rebind to ScriptExecutor if it is null
370 mDataBroker.scheduleNextTask();
371
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700372 waitForTelemetryThreadToFinish();
Rui Qiuaf750102021-09-17 13:27:23 -0700373 assertThat(taskQueue.peek()).isNull();
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700374 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Rui Qiuaf750102021-09-17 13:27:23 -0700375 }
376
377 @Test
378 public void testScheduleNextTask_bindScriptExecutorFailedMultipleTimes_shouldDisableBroker()
379 throws Exception {
380 // fail 6 future attempts to bind to it
381 Mockito.reset(mMockContext);
382 when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any()))
383 .thenReturn(false, false, false, false, false, false);
384 mDataBroker.mBindScriptExecutorDelayMillis = 0L; // immediately rebind for testing purpose
385 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
386 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
387 taskQueue.add(mHighPriorityTask);
388
389 // will rebind to ScriptExecutor if it is null
390 mDataBroker.scheduleNextTask();
391
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700392 waitForTelemetryThreadToFinish();
Rui Qiuaf750102021-09-17 13:27:23 -0700393 // broker disabled, all subscribers should have been removed
Rui Qiua86ab602021-07-02 10:40:36 -0700394 assertThat(mDataBroker.getSubscriptionMap()).hasSize(0);
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700395 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(0);
Rui Qiua86ab602021-07-02 10:40:36 -0700396 }
397
398 @Test
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700399 public void testScheduleNextTask_whenScriptExecutorThrowsException_shouldResetAndTryAgain()
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700400 throws Exception {
Rui Qiua86ab602021-07-02 10:40:36 -0700401 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
402 taskQueue.add(mHighPriorityTask);
403 mFakeScriptExecutor.failNextApiCalls(1); // fail the next invokeScript() call
404
405 mDataBroker.scheduleNextTask();
406
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700407 waitForTelemetryThreadToFinish();
408 // invokeScript() failed, task is re-queued and re-run
409 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(2);
410 assertThat(taskQueue).isEmpty();
Rui Qiu033041e2021-06-21 14:59:11 -0700411 }
412
413 @Test
Rui Qiuac8b5ba2021-08-25 16:04:17 -0700414 public void testAddTaskToQueue_shouldInvokeScriptExecutor() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700415 mDataBroker.addTaskToQueue(mHighPriorityTask);
416
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700417 waitForTelemetryThreadToFinish();
418 assertThat(mFakeScriptExecutor.getInvokeScriptCount()).isEqualTo(1);
Zhomart Mukhamejanov44570cb2021-06-07 10:23:13 -0700419 }
420
Rui Qiu337cb242021-06-03 11:26:16 -0700421 @Test
422 public void testAddMetricsConfiguration_newMetricsConfig() {
Rui Qiuf14c6a22021-07-14 10:39:16 -0700423 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
424
Rui Qiuf14c6a22021-07-14 10:39:16 -0700425 assertThat(mDataBroker.getSubscriptionMap()).hasSize(1);
426 assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_BAR.getName());
427 // there should be one data subscriber in the subscription list of METRICS_CONFIG_BAR
428 assertThat(mDataBroker.getSubscriptionMap().get(METRICS_CONFIG_BAR.getName())).hasSize(1);
429 }
430
431
432 @Test
433 public void testAddMetricsConfiguration_duplicateMetricsConfig_shouldDoNothing() {
434 // this metrics config has already been added in setUp()
Rui Qiu337cb242021-06-03 11:26:16 -0700435 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
436
Rui Qiuf14c6a22021-07-14 10:39:16 -0700437 assertThat(mDataBroker.getSubscriptionMap()).hasSize(1);
Rui Qiu337cb242021-06-03 11:26:16 -0700438 assertThat(mDataBroker.getSubscriptionMap()).containsKey(METRICS_CONFIG_FOO.getName());
Rui Qiu337cb242021-06-03 11:26:16 -0700439 assertThat(mDataBroker.getSubscriptionMap().get(METRICS_CONFIG_FOO.getName())).hasSize(1);
440 }
441
442 @Test
Rui Qiuf14c6a22021-07-14 10:39:16 -0700443 public void testRemoveMetricsConfiguration_shouldRemoveAllAssociatedTasks() {
Rui Qiu337cb242021-06-03 11:26:16 -0700444 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
445 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
Rui Qiud95f95d2021-08-17 12:29:33 -0700446 ScriptExecutionTask taskWithMetricsConfigFoo = new ScriptExecutionTask(
447 new DataSubscriber(mDataBroker, METRICS_CONFIG_FOO, SUBSCRIBER_FOO),
Rui Qiuea6a4942021-09-07 15:05:34 -0700448 mData,
Rui Qiuf14c6a22021-07-14 10:39:16 -0700449 SystemClock.elapsedRealtime());
450 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
451 taskQueue.add(mHighPriorityTask); // associated with METRICS_CONFIG_FOO
Rui Qiud95f95d2021-08-17 12:29:33 -0700452 taskQueue.add(mLowPriorityTask); // associated with METRICS_CONFIG_BAR
453 taskQueue.add(taskWithMetricsConfigFoo); // associated with METRICS_CONFIG_FOO
Rui Qiuf14c6a22021-07-14 10:39:16 -0700454 assertThat(taskQueue).hasSize(3);
455
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700456 mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_FOO.getName());
Rui Qiu337cb242021-06-03 11:26:16 -0700457
Rui Qiuf14c6a22021-07-14 10:39:16 -0700458 assertThat(taskQueue).hasSize(1);
Rui Qiud95f95d2021-08-17 12:29:33 -0700459 assertThat(taskQueue.poll()).isEqualTo(mLowPriorityTask);
Rui Qiuf14c6a22021-07-14 10:39:16 -0700460 }
461
462 @Test
463 public void testRemoveMetricsConfiguration_whenMetricsConfigNonExistent_shouldDoNothing() {
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700464 mDataBroker.removeMetricsConfiguration(METRICS_CONFIG_BAR.getName());
Rui Qiuf14c6a22021-07-14 10:39:16 -0700465
Rui Qiuf14c6a22021-07-14 10:39:16 -0700466 assertThat(mDataBroker.getSubscriptionMap()).hasSize(0);
Rui Qiu337cb242021-06-03 11:26:16 -0700467 }
468
Rui Qiu9f80c7c2021-09-28 15:15:36 -0700469 @Test
470 public void testRemoveAllMetricsConfigurations_shouldRemoveTasksAndClearSubscriptionMap() {
471 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_FOO);
472 mDataBroker.addMetricsConfiguration(METRICS_CONFIG_BAR);
473 PriorityBlockingQueue<ScriptExecutionTask> taskQueue = mDataBroker.getTaskQueue();
474 taskQueue.add(mHighPriorityTask); // associated with METRICS_CONFIG_FOO
475 taskQueue.add(mLowPriorityTask); // associated with METRICS_CONFIG_BAR
476
477 mDataBroker.removeAllMetricsConfigurations();
478
479 assertThat(taskQueue).isEmpty();
480 assertThat(mDataBroker.getSubscriptionMap()).isEmpty();
481 }
482
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700483 private void waitForTelemetryThreadToFinish() throws Exception {
Rui Qiu033041e2021-06-21 14:59:11 -0700484 assertWithMessage("handler not idle in %sms", TIMEOUT_MS)
Rui Qiua4a8fd02021-09-02 19:08:40 -0700485 .that(mIdleHandlerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
Rui Qiuea6a4942021-09-07 15:05:34 -0700486 mIdleHandlerLatch = new CountDownLatch(1); // reset idle handler condition
Rui Qiu337cb242021-06-03 11:26:16 -0700487 }
Rui Qiua86ab602021-07-02 10:40:36 -0700488
489 private static class FakeScriptExecutor implements IScriptExecutor {
490 private IScriptExecutorListener mListener;
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700491 private int mInvokeScriptCount = 0;
492 private int mInvokeScriptForLargeInputCount = 0;
Rui Qiua86ab602021-07-02 10:40:36 -0700493 private int mFailApi = 0;
Rui Qiuea6a4942021-09-07 15:05:34 -0700494 private PersistableBundle mSavedState = null;
Rui Qiua86ab602021-07-02 10:40:36 -0700495
496 @Override
Max Dashoukdafdd222021-08-20 10:42:25 -0700497 public void invokeScript(String scriptBody, String functionName,
498 PersistableBundle publishedData, @Nullable PersistableBundle savedState,
499 IScriptExecutorListener listener)
Rui Qiua86ab602021-07-02 10:40:36 -0700500 throws RemoteException {
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700501 mInvokeScriptCount++;
Rui Qiuea6a4942021-09-07 15:05:34 -0700502 mSavedState = savedState;
Rui Qiua86ab602021-07-02 10:40:36 -0700503 mListener = listener;
504 if (mFailApi > 0) {
505 mFailApi--;
506 throw new RemoteException("Simulated failure");
507 }
508 }
509
510 @Override
Max Dashouk6987e9c2021-09-20 13:19:30 -0700511 public void invokeScriptForLargeInput(String scriptBody, String functionName,
512 ParcelFileDescriptor publishedDataFileDescriptor,
513 @Nullable PersistableBundle savedState,
514 IScriptExecutorListener listener) throws RemoteException {
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700515 mInvokeScriptForLargeInputCount++;
Max Dashouk6987e9c2021-09-20 13:19:30 -0700516 mSavedState = savedState;
517 mListener = listener;
518 if (mFailApi > 0) {
519 mFailApi--;
520 throw new RemoteException("Simulated failure");
521 }
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700522 // Since DataBrokerImpl and FakeScriptExecutor are in the same process, they do not
523 // use real IPC and share the fd. When DataBroker closes the fd, it affects
524 // FakeScriptExecutor. Therefore FakeScriptExecutor must dup the fd before it is
525 // closed by DataBroker
526 ParcelFileDescriptor dup = null;
527 try {
528 dup = publishedDataFileDescriptor.dup();
529 } catch (IOException e) { }
530 final ParcelFileDescriptor fd = Objects.requireNonNull(dup);
531 // to prevent deadlock, read and write must happen on separate threads
532 Handler.getMain().post(() -> {
533 try (InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
534 PersistableBundle.readFromStream(input);
535 } catch (IOException e) { }
536 });
Max Dashouk6987e9c2021-09-20 13:19:30 -0700537 }
538
539 @Override
Rui Qiua86ab602021-07-02 10:40:36 -0700540 public IBinder asBinder() {
541 return null;
542 }
543
Rui Qiuaba02082021-08-16 16:24:00 -0700544 /** Mocks script temporary completion. */
Max Dashoukdafdd222021-08-20 10:42:25 -0700545 public void notifyScriptSuccess(PersistableBundle bundle) {
Rui Qiua86ab602021-07-02 10:40:36 -0700546 try {
Rui Qiuaba02082021-08-16 16:24:00 -0700547 mListener.onSuccess(bundle);
Rui Qiua86ab602021-07-02 10:40:36 -0700548 } catch (RemoteException e) {
549 // nothing to do
550 }
551 }
552
Rui Qiuea6a4942021-09-07 15:05:34 -0700553 /** Mocks script producing final result. */
554 public void notifyScriptFinish(PersistableBundle bundle) {
555 try {
556 mListener.onScriptFinished(bundle);
557 } catch (RemoteException e) {
558 // nothing to do
559 }
560 }
561
Rui Qiu862d6ea2021-08-17 15:00:09 -0700562 /** Mocks script finished with error. */
563 public void notifyScriptError(int errorType, String errorMessage) {
564 try {
565 mListener.onError(errorType, errorMessage, null);
566 } catch (RemoteException e) {
567 // nothing to do
568 }
569 }
570
Rui Qiua86ab602021-07-02 10:40:36 -0700571 /** Fails the next N invokeScript() call. */
572 public void failNextApiCalls(int n) {
573 mFailApi = n;
574 }
575
Rui Qiuf4aa2e52021-09-27 16:30:00 -0700576 /** Returns number of times invokeScript() was called. */
577 public int getInvokeScriptCount() {
578 return mInvokeScriptCount;
579 }
580
581 /** Returns number of times invokeScriptForLargeInput() was called. */
582 public int getInvokeScriptForLargeInputCount() {
583 return mInvokeScriptForLargeInputCount;
Rui Qiua86ab602021-07-02 10:40:36 -0700584 }
Rui Qiuea6a4942021-09-07 15:05:34 -0700585
586 /** Returns the interim data passed in invokeScript(). */
587 public PersistableBundle getSavedState() {
588 return mSavedState;
589 }
Rui Qiua86ab602021-07-02 10:40:36 -0700590 }
Rui Qiu337cb242021-06-03 11:26:16 -0700591}