blob: 56ce568d0851439eba1c1ec2480aaf5b47f6e2e2 [file] [log] [blame]
Pavel Maltsev82c20cd2017-04-11 12:38:17 -07001/*
2 * Copyright (C) 2017 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 */
16package com.android.car.vehiclehal.test;
17
18import static org.junit.Assert.assertEquals;
Pavel Maltsev45e59a02017-04-20 13:05:26 -070019import static org.junit.Assert.assertFalse;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070020import static org.junit.Assert.assertNotNull;
21import static org.junit.Assert.assertTrue;
Pavel Maltsevbe9a4102017-09-20 13:57:00 -070022import static org.junit.Assert.fail;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070023
Pavel Maltsev45e59a02017-04-20 13:05:26 -070024import android.annotation.Nullable;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070025import android.car.Car;
Pavel Maltsev45e59a02017-04-20 13:05:26 -070026import android.car.hardware.CarPropertyConfig;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070027import android.car.hardware.CarSensorManager;
28import android.car.hardware.CarSensorManager.OnSensorChangedListener;
Pavel Maltsev45e59a02017-04-20 13:05:26 -070029import android.car.hardware.hvac.CarHvacManager;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070030import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
Pavel Maltsev45e59a02017-04-20 13:05:26 -070031import android.os.SystemClock;
Pavel Maltsevbe9a4102017-09-20 13:57:00 -070032import android.util.Log;
Pavel Maltsev45e59a02017-04-20 13:05:26 -070033import android.util.SparseArray;
34import android.util.SparseIntArray;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070035
Brett Chabota81f3e82018-12-13 19:06:42 -080036import androidx.test.filters.MediumTest;
37import androidx.test.runner.AndroidJUnit4;
38
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070039import com.google.android.collect.Lists;
40
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070041import org.junit.Test;
42import org.junit.runner.RunWith;
43
Brett Chabota81f3e82018-12-13 19:06:42 -080044import static java.lang.Integer.toHexString;
45
Pavel Maltsev45e59a02017-04-20 13:05:26 -070046import java.util.ArrayList;
47import java.util.List;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070048import java.util.concurrent.CountDownLatch;
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070049import java.util.concurrent.TimeUnit;
50
51/**
52 * This test suite will make e2e test and measure some performance characteristics. The main idea
53 * is to send command to Vehicle HAL to generate some events with certain time interval and capture
54 * these events through car public API, e.g. CarSensorManager.
Pavel Maltsev45e59a02017-04-20 13:05:26 -070055 *
56 * TODO(pavelm): benchmark tests might be flaky, need a way to run them multiple times / use avg
57 * metrics.
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070058 */
Pavel Maltsev45e59a02017-04-20 13:05:26 -070059@MediumTest
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070060@RunWith(AndroidJUnit4.class)
Chao Yanf2c109c2018-05-11 12:16:14 -070061public class E2ePerformanceTest extends E2eCarTestBase {
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070062 private static String TAG = Utils.concatTag(E2ePerformanceTest.class);
63
Pavel Maltsev82c20cd2017-04-11 12:38:17 -070064 @Test
65 public void singleOnChangeProperty() throws Exception {
Pavel Maltsev45e59a02017-04-20 13:05:26 -070066 verifyEventsFromSingleProperty(
67 CarSensorManager.SENSOR_TYPE_ODOMETER, VehicleProperty.PERF_ODOMETER);
68 }
69
70 @Test
71 public void singleContinuousProperty() throws Exception {
72 verifyEventsFromSingleProperty(
Steve Paike58f2972018-03-02 11:38:40 -080073 CarSensorManager.SENSOR_TYPE_CAR_SPEED, VehicleProperty.PERF_VEHICLE_SPEED);
Pavel Maltsev45e59a02017-04-20 13:05:26 -070074 }
75
76 @Test
77 public void benchmarkEventBandwidthThroughCarService() throws Exception {
78 int[] mgrProperties = new int[] {
79 CarSensorManager.SENSOR_TYPE_ODOMETER,
Pavel Maltsev45e59a02017-04-20 13:05:26 -070080 CarSensorManager.SENSOR_TYPE_CAR_SPEED
81 };
82 // Expecting to receive at least 10 events within 150ms.
83 final int EVENT_INTERVAL_MS = 1; //
84 final int EXPECTED_EVENTS_PER_PROPERTY = 1000;
85 final int EXPECTED_EVENTS = EXPECTED_EVENTS_PER_PROPERTY * mgrProperties.length;
86
87 CarSensorManager mgr = (CarSensorManager) mCar.getCarManager(Car.SENSOR_SERVICE);
88 assertNotNull(mgr);
89 for (int mgrPropId: mgrProperties) {
90 assertTrue("PropId: 0x" + toHexString(mgrPropId) + " is not supported",
91 mgr.isSensorSupported(mgrPropId));
92 }
93
Chao Yanf2c109c2018-05-11 12:16:14 -070094 VhalEventGenerator odometerGenerator = new LinearVhalEventGenerator(mVehicle)
95 .setProp(VehicleProperty.PERF_ODOMETER)
Pavel Maltsev45e59a02017-04-20 13:05:26 -070096 .setIntervalMs(EVENT_INTERVAL_MS)
97 .setInitialValue(1000)
98 .setIncrement(1.0f)
Chao Yanf2c109c2018-05-11 12:16:14 -070099 .setDispersion(100);
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700100
Chao Yanf2c109c2018-05-11 12:16:14 -0700101
102 VhalEventGenerator speedGenerator = new LinearVhalEventGenerator(mVehicle)
103 .setProp(VehicleProperty.PERF_VEHICLE_SPEED)
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700104 .setIntervalMs(EVENT_INTERVAL_MS)
105 .setInitialValue(20.0f)
106 .setIncrement(0.1f)
Chao Yanf2c109c2018-05-11 12:16:14 -0700107 .setDispersion(10);
108
109 odometerGenerator.start();
110 speedGenerator.start();
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700111
112 SparseArray<CountDownLatch> eventsCounters = new SparseArray<>();
113 for (int i = 0; i < mgrProperties.length; i++) {
114 eventsCounters.put(mgrProperties[i], new CountDownLatch(EXPECTED_EVENTS_PER_PROPERTY));
115 }
116 OnSensorChangedListener listener = e -> eventsCounters.get(e.sensorType).countDown();
117 for (int propId: mgrProperties) {
118 mgr.registerListener(listener, propId, CarSensorManager.SENSOR_RATE_FASTEST);
119 }
120
121 final long WAIT_TIME = (long) ((EVENT_INTERVAL_MS * EXPECTED_EVENTS_PER_PROPERTY) * 1.6);
122 CountDownLatch[] latches = new CountDownLatch[eventsCounters.size()];
123 for (int i = 0; i < eventsCounters.size(); i++) {
124 latches[i] = eventsCounters.valueAt(i);
125 }
126 boolean allEventsReceived = awaitCountDownLatches(latches, WAIT_TIME);
127 mgr.unregisterListener(listener);
Chao Yanf2c109c2018-05-11 12:16:14 -0700128
129 odometerGenerator.stop();
130 speedGenerator.stop();
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700131
132 if (!allEventsReceived) {
133 SparseIntArray missingEventsPerProperty = new SparseIntArray();
134 for (int i = 0; i < eventsCounters.size(); i++) {
135 int missingEvents = (int) eventsCounters.valueAt(i).getCount();
136 if (missingEvents > 0) {
137 missingEventsPerProperty.put(eventsCounters.keyAt(i), missingEvents);
138 }
139 }
140
141 assertTrue("Too slow. Expected to receive: " + EXPECTED_EVENTS
142 + " within " + WAIT_TIME + " ms, "
143 + " missing events per property: " + missingEventsPerProperty,
144 missingEventsPerProperty.size() == 0);
145 }
146 }
147
148 @Test
149 public void benchmarkSetGetFromSingleClient() throws Exception {
150 final int PROP = CarHvacManager.ID_WINDOW_DEFROSTER_ON;
151 CarHvacManager mgr = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
152 assertNotNull(mgr);
153 long start = SystemClock.elapsedRealtimeNanos();
154
155 final long TEST_DURATION_NANO = 1_000_000_000; // 1 second.
156 final long EXPECTED_ITERATIONS = 100; // We expect to have at least 100 get/set calls.
157
158 boolean value = false;
159 long actualIterations = 0;
160 while (SystemClock.elapsedRealtimeNanos() < start + TEST_DURATION_NANO) {
Steve Paik5e313ad2018-04-04 12:40:33 -0700161 mgr.setBooleanProperty(PROP, 1, value);
162 boolean actualValue = mgr.getBooleanProperty(PROP, 1);
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700163 assertEquals(value, actualValue);
164 value = !value;
165 actualIterations++;
166 }
167 assertTrue("Too slow. Expected iterations: " + EXPECTED_ITERATIONS
168 + ", actual: " + actualIterations
169 + ", test duration: " + TEST_DURATION_NANO / 1000_1000 + " ms.",
170 actualIterations >= EXPECTED_ITERATIONS);
171 }
172
173 @Test
174 public void benchmarkSetGetFromSingleClientMultipleThreads() throws Exception {
175 final int PROP = CarHvacManager.ID_ZONED_TEMP_SETPOINT;
176 CarHvacManager mgr = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
177 assertNotNull(mgr);
178
179 CarPropertyConfig<Float> cfg = findHvacPropConfig(Float.class, PROP, mgr);
180 assertNotNull(cfg);
181 assertTrue("Expected at least 2 zones for 0x" + Integer.toHexString(PROP)
182 + ", got: " + cfg.getAreaCount(), cfg.getAreaCount() >= 2);
183
184
185 final int EXPECTED_INVOCATIONS = 1000; // How many time get/set will be called.
186 final int EXPECTED_DURATION_MS = 2500;
Pavel Maltsevbe9a4102017-09-20 13:57:00 -0700187 // This is a stress test and it can be flaky because it shares resources with all currently
188 // running process. Let's have this number of attempt before giving up.
189 final int ATTEMPTS = 3;
190
191 for (int curAttempt = 0; curAttempt < ATTEMPTS; curAttempt++) {
192 long missingInvocations = stressTestHvacProperties(mgr, cfg,
193 EXPECTED_INVOCATIONS, EXPECTED_DURATION_MS);
194 if (missingInvocations == 0) return; // All done.
195
196 Log.w(TAG, "Failed to invoke get/set " + EXPECTED_INVOCATIONS
197 + " within " + EXPECTED_DURATION_MS + "ms"
198 + ", actually invoked: "
199 + (EXPECTED_INVOCATIONS - missingInvocations));
200 }
201 fail("Failed to invoke get/set " + EXPECTED_INVOCATIONS + " within "
202 + EXPECTED_DURATION_MS + "ms. Number of attempts: " + ATTEMPTS
203 + ". See logs for details.");
204 }
205
206 private long stressTestHvacProperties(CarHvacManager mgr, CarPropertyConfig<Float> cfg,
207 int EXPECTED_INVOCATIONS, int EXPECTED_DURATION_MS) throws InterruptedException {
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700208 CountDownLatch counter = new CountDownLatch(EXPECTED_INVOCATIONS);
209
210 List<Thread> threads = new ArrayList<>(Lists.newArrayList(
211 new Thread(() -> invokeSetAndGetForHvacFloat(mgr, cfg, cfg.getAreaIds()[0], counter)),
212 new Thread(() -> invokeSetAndGetForHvacFloat(mgr, cfg, cfg.getAreaIds()[1], counter))));
213
214 for (Thread t : threads) {
215 t.start();
216 }
217
218 counter.await(EXPECTED_DURATION_MS, TimeUnit.MILLISECONDS);
219 long missingInvocations = counter.getCount();
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700220
221 for (Thread t : threads) {
222 t.join(10000); // Let thread join to not interfere with other test.
223 assertFalse(t.isAlive());
224 }
Pavel Maltsevbe9a4102017-09-20 13:57:00 -0700225 return missingInvocations;
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700226 }
227
228 private void invokeSetAndGetForHvacFloat(CarHvacManager mgr,
229 CarPropertyConfig<Float> cfg, int areaId, CountDownLatch counter) {
230 float minValue = cfg.getMinValue(areaId);
231 float maxValue = cfg.getMaxValue(areaId);
232 float curValue = minValue;
233
234 while (counter.getCount() > 0) {
235 float actualValue;
Justin Paupore97b4df72019-02-12 19:40:39 -0800236 mgr.setFloatProperty(cfg.getPropertyId(), areaId, curValue);
237 actualValue = mgr.getFloatProperty(cfg.getPropertyId(), areaId);
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700238 assertEquals(curValue, actualValue, 0.001);
239 curValue += 0.5;
240 if (curValue > maxValue) {
241 curValue = minValue;
242 }
243
244 counter.countDown();
245 }
246 }
247
248 @Nullable
Justin Paupore97b4df72019-02-12 19:40:39 -0800249 private <T> CarPropertyConfig<T> findHvacPropConfig(
250 Class<T> clazz, int hvacPropId, CarHvacManager mgr) {
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700251 for (CarPropertyConfig<?> cfg : mgr.getPropertyList()) {
252 if (cfg.getPropertyId() == hvacPropId) {
253 return (CarPropertyConfig<T>) cfg;
254 }
255 }
256 return null;
257 }
258
259 private void verifyEventsFromSingleProperty(int mgrPropId, int halPropId) throws Exception {
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700260 // Expecting to receive at least 10 events within 150ms.
261 final int EXPECTED_EVENTS = 10;
262 final int EXPECTED_TIME_DURATION_MS = 150;
263 final float INITIAL_VALUE = 1000;
264 final float INCREMENT = 1.0f;
265
266 CarSensorManager mgr = (CarSensorManager) mCar.getCarManager(Car.SENSOR_SERVICE);
267 assertNotNull(mgr);
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700268 assertTrue(mgr.isSensorSupported(mgrPropId));
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700269
Chao Yanf2c109c2018-05-11 12:16:14 -0700270 VhalEventGenerator generator = new LinearVhalEventGenerator(mVehicle)
271 .setProp(halPropId)
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700272 .setIntervalMs(10)
273 .setInitialValue(INITIAL_VALUE)
274 .setIncrement(INCREMENT)
Chao Yanf2c109c2018-05-11 12:16:14 -0700275 .setDispersion(100);
276
277 generator.start();
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700278
279 CountDownLatch latch = new CountDownLatch(EXPECTED_EVENTS);
280 OnSensorChangedListener listener = event -> latch.countDown();
281
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700282 mgr.registerListener(listener, mgrPropId, CarSensorManager.SENSOR_RATE_FASTEST);
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700283 try {
284 assertTrue(latch.await(EXPECTED_TIME_DURATION_MS, TimeUnit.MILLISECONDS));
285 } finally {
Chao Yanf2c109c2018-05-11 12:16:14 -0700286 generator.stop();
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700287 mgr.unregisterListener(listener);
288 }
289 }
290
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700291
Pavel Maltsev45e59a02017-04-20 13:05:26 -0700292 private static boolean awaitCountDownLatches(CountDownLatch[] latches, long timeoutMs)
293 throws InterruptedException {
294 long start = SystemClock.elapsedRealtime();
295 for (int i = 0; i < latches.length; i++) {
296 long timeLeft = timeoutMs - (SystemClock.elapsedRealtime() - start);
297 if (!latches[i].await(timeLeft, TimeUnit.MILLISECONDS)) {
298 return false;
299 }
300 }
301
302 return true;
303 }
Pavel Maltsev82c20cd2017-04-11 12:38:17 -0700304}