aboutsummaryrefslogtreecommitdiff
path: root/tests/vehiclehal_test
diff options
context:
space:
mode:
authorPavel Maltsev <pavelm@google.com>2017-04-20 13:05:26 -0700
committerPavel Maltsev <pavelm@google.com>2017-04-20 19:33:41 -0700
commit45e59a01ae0d00694743b49b6e3135ad97557e6f (patch)
treed6d6cca63d7a3e7edb456efb9ebc6fdd61f5f2fc /tests/vehiclehal_test
parent54558e083dcbb0a7a37604793ffe057fb8de13eb (diff)
downloadCar-45e59a01ae0d00694743b49b6e3135ad97557e6f.tar.gz
Adding more benchmark tests for Vehicle stack
Also, change mapping of predefined frequence, if client specify SENSOR_RATE_FASTEST us maximum available rate from vehicle property configuration. Allow users to use all predefined SENSOR_RATE_* Test: runtest -x packages/services/Car/test/vehiclehal_test Bug: b/36510399 Change-Id: Ica466c0ff35161cd1e580ad3da2c87d8b844795e Fix: b/37538698
Diffstat (limited to 'tests/vehiclehal_test')
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java218
1 files changed, 212 insertions, 6 deletions
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java
index c76cc4ae81..6675713f47 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java
@@ -15,14 +15,20 @@
*/
package com.android.car.vehiclehal.test;
+import static java.lang.Integer.toHexString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import android.annotation.Nullable;
import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarSensorManager;
import android.car.hardware.CarSensorManager.OnSensorChangedListener;
+import android.car.hardware.hvac.CarHvacManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
@@ -37,9 +43,12 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.google.android.collect.Lists;
@@ -51,6 +60,8 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -59,8 +70,11 @@ import java.util.concurrent.TimeUnit;
* This test suite will make e2e test and measure some performance characteristics. The main idea
* is to send command to Vehicle HAL to generate some events with certain time interval and capture
* these events through car public API, e.g. CarSensorManager.
+ *
+ * TODO(pavelm): benchmark tests might be flaky, need a way to run them multiple times / use avg
+ * metrics.
*/
-@LargeTest
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class E2ePerformanceTest {
private static String TAG = Utils.concatTag(E2ePerformanceTest.class);
@@ -125,7 +139,186 @@ public class E2ePerformanceTest {
@Test
public void singleOnChangeProperty() throws Exception {
- final int PROP = CarSensorManager.SENSOR_TYPE_ODOMETER;
+ verifyEventsFromSingleProperty(
+ CarSensorManager.SENSOR_TYPE_ODOMETER, VehicleProperty.PERF_ODOMETER);
+ }
+
+ @Test
+ public void singleContinuousProperty() throws Exception {
+ verifyEventsFromSingleProperty(
+ CarSensorManager.SENSOR_TYPE_RPM, VehicleProperty.ENGINE_RPM);
+ }
+
+ @Test
+ public void benchmarkEventBandwidthThroughCarService() throws Exception {
+ int[] mgrProperties = new int[] {
+ CarSensorManager.SENSOR_TYPE_ODOMETER,
+ CarSensorManager.SENSOR_TYPE_RPM,
+ CarSensorManager.SENSOR_TYPE_CAR_SPEED
+ };
+ // Expecting to receive at least 10 events within 150ms.
+ final int EVENT_INTERVAL_MS = 1; //
+ final int EXPECTED_EVENTS_PER_PROPERTY = 1000;
+ final int EXPECTED_EVENTS = EXPECTED_EVENTS_PER_PROPERTY * mgrProperties.length;
+
+ CarSensorManager mgr = (CarSensorManager) mCar.getCarManager(Car.SENSOR_SERVICE);
+ assertNotNull(mgr);
+ for (int mgrPropId: mgrProperties) {
+ assertTrue("PropId: 0x" + toHexString(mgrPropId) + " is not supported",
+ mgr.isSensorSupported(mgrPropId));
+ }
+
+ mEventsGenerator
+ .reset()
+ .setIntervalMs(EVENT_INTERVAL_MS)
+ .setInitialValue(1000)
+ .setIncrement(1.0f)
+ .setDispersion(100)
+ .start(VehicleProperty.PERF_ODOMETER);
+
+ mEventsGenerator
+ .start(VehicleProperty.ENGINE_RPM);
+
+ mEventsGenerator
+ .setIntervalMs(EVENT_INTERVAL_MS)
+ .setInitialValue(20.0f)
+ .setIncrement(0.1f)
+ .setDispersion(10)
+ .start(VehicleProperty.PERF_VEHICLE_SPEED);
+
+ SparseArray<CountDownLatch> eventsCounters = new SparseArray<>();
+ for (int i = 0; i < mgrProperties.length; i++) {
+ eventsCounters.put(mgrProperties[i], new CountDownLatch(EXPECTED_EVENTS_PER_PROPERTY));
+ }
+ OnSensorChangedListener listener = e -> eventsCounters.get(e.sensorType).countDown();
+ for (int propId: mgrProperties) {
+ mgr.registerListener(listener, propId, CarSensorManager.SENSOR_RATE_FASTEST);
+ }
+
+ final long WAIT_TIME = (long) ((EVENT_INTERVAL_MS * EXPECTED_EVENTS_PER_PROPERTY) * 1.6);
+ CountDownLatch[] latches = new CountDownLatch[eventsCounters.size()];
+ for (int i = 0; i < eventsCounters.size(); i++) {
+ latches[i] = eventsCounters.valueAt(i);
+ }
+ boolean allEventsReceived = awaitCountDownLatches(latches, WAIT_TIME);
+ mgr.unregisterListener(listener);
+ mEventsGenerator.stop();
+
+ if (!allEventsReceived) {
+ SparseIntArray missingEventsPerProperty = new SparseIntArray();
+ for (int i = 0; i < eventsCounters.size(); i++) {
+ int missingEvents = (int) eventsCounters.valueAt(i).getCount();
+ if (missingEvents > 0) {
+ missingEventsPerProperty.put(eventsCounters.keyAt(i), missingEvents);
+ }
+ }
+
+ assertTrue("Too slow. Expected to receive: " + EXPECTED_EVENTS
+ + " within " + WAIT_TIME + " ms, "
+ + " missing events per property: " + missingEventsPerProperty,
+ missingEventsPerProperty.size() == 0);
+ }
+ }
+
+ @Test
+ public void benchmarkSetGetFromSingleClient() throws Exception {
+ final int PROP = CarHvacManager.ID_WINDOW_DEFROSTER_ON;
+ CarHvacManager mgr = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+ assertNotNull(mgr);
+ long start = SystemClock.elapsedRealtimeNanos();
+
+ final long TEST_DURATION_NANO = 1_000_000_000; // 1 second.
+ final long EXPECTED_ITERATIONS = 100; // We expect to have at least 100 get/set calls.
+
+ boolean value = false;
+ long actualIterations = 0;
+ while (SystemClock.elapsedRealtimeNanos() < start + TEST_DURATION_NANO) {
+ mgr.setBooleanProperty(PROP, 0, value);
+ boolean actualValue = mgr.getBooleanProperty(PROP, 0);
+ assertEquals(value, actualValue);
+ value = !value;
+ actualIterations++;
+ }
+ assertTrue("Too slow. Expected iterations: " + EXPECTED_ITERATIONS
+ + ", actual: " + actualIterations
+ + ", test duration: " + TEST_DURATION_NANO / 1000_1000 + " ms.",
+ actualIterations >= EXPECTED_ITERATIONS);
+ }
+
+ @Test
+ public void benchmarkSetGetFromSingleClientMultipleThreads() throws Exception {
+ final int PROP = CarHvacManager.ID_ZONED_TEMP_SETPOINT;
+ CarHvacManager mgr = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+ assertNotNull(mgr);
+
+ CarPropertyConfig<Float> cfg = findHvacPropConfig(Float.class, PROP, mgr);
+ assertNotNull(cfg);
+ assertTrue("Expected at least 2 zones for 0x" + Integer.toHexString(PROP)
+ + ", got: " + cfg.getAreaCount(), cfg.getAreaCount() >= 2);
+
+
+ final int EXPECTED_INVOCATIONS = 1000; // How many time get/set will be called.
+ final int EXPECTED_DURATION_MS = 2500;
+ CountDownLatch counter = new CountDownLatch(EXPECTED_INVOCATIONS);
+
+ List<Thread> threads = new ArrayList<>(Lists.newArrayList(
+ new Thread(() -> invokeSetAndGetForHvacFloat(mgr, cfg, cfg.getAreaIds()[0], counter)),
+ new Thread(() -> invokeSetAndGetForHvacFloat(mgr, cfg, cfg.getAreaIds()[1], counter))));
+
+ for (Thread t : threads) {
+ t.start();
+ }
+
+ counter.await(EXPECTED_DURATION_MS, TimeUnit.MILLISECONDS);
+ long missingInvocations = counter.getCount();
+ assertTrue("Failed to invoke get/set " + EXPECTED_INVOCATIONS
+ + " within " + EXPECTED_DURATION_MS + "ms"
+ + ", actually invoked: " + (EXPECTED_INVOCATIONS - missingInvocations),
+ missingInvocations == 0);
+
+ for (Thread t : threads) {
+ t.join(10000); // Let thread join to not interfere with other test.
+ assertFalse(t.isAlive());
+ }
+ }
+
+ private void invokeSetAndGetForHvacFloat(CarHvacManager mgr,
+ CarPropertyConfig<Float> cfg, int areaId, CountDownLatch counter) {
+ float minValue = cfg.getMinValue(areaId);
+ float maxValue = cfg.getMaxValue(areaId);
+ float curValue = minValue;
+
+ while (counter.getCount() > 0) {
+ float actualValue;
+ try {
+ mgr.setFloatProperty(cfg.getPropertyId(), areaId, curValue);
+ actualValue = mgr.getFloatProperty(cfg.getPropertyId(), areaId);
+ } catch (CarNotConnectedException e) {
+ throw new RuntimeException(e);
+ }
+ assertEquals(curValue, actualValue, 0.001);
+ curValue += 0.5;
+ if (curValue > maxValue) {
+ curValue = minValue;
+ }
+
+ counter.countDown();
+ }
+ }
+
+ @Nullable
+ private <T> CarPropertyConfig<T> findHvacPropConfig(Class<T> clazz, int hvacPropId,
+ CarHvacManager mgr)
+ throws CarNotConnectedException {
+ for (CarPropertyConfig<?> cfg : mgr.getPropertyList()) {
+ if (cfg.getPropertyId() == hvacPropId) {
+ return (CarPropertyConfig<T>) cfg;
+ }
+ }
+ return null;
+ }
+
+ private void verifyEventsFromSingleProperty(int mgrPropId, int halPropId) throws Exception {
// Expecting to receive at least 10 events within 150ms.
final int EXPECTED_EVENTS = 10;
final int EXPECTED_TIME_DURATION_MS = 150;
@@ -134,19 +327,19 @@ public class E2ePerformanceTest {
CarSensorManager mgr = (CarSensorManager) mCar.getCarManager(Car.SENSOR_SERVICE);
assertNotNull(mgr);
- assertTrue(mgr.isSensorSupported(CarSensorManager.SENSOR_TYPE_ODOMETER));
+ assertTrue(mgr.isSensorSupported(mgrPropId));
mEventsGenerator
.setIntervalMs(10)
.setInitialValue(INITIAL_VALUE)
.setIncrement(INCREMENT)
.setDispersion(100)
- .start(VehicleProperty.PERF_ODOMETER);
+ .start(halPropId);
CountDownLatch latch = new CountDownLatch(EXPECTED_EVENTS);
OnSensorChangedListener listener = event -> latch.countDown();
- mgr.registerListener(listener, PROP, CarSensorManager.SENSOR_RATE_FASTEST);
+ mgr.registerListener(listener, mgrPropId, CarSensorManager.SENSOR_RATE_FASTEST);
try {
assertTrue(latch.await(EXPECTED_TIME_DURATION_MS, TimeUnit.MILLISECONDS));
} finally {
@@ -170,6 +363,19 @@ public class E2ePerformanceTest {
public void onServiceDisconnected(ComponentName name) { }
}
+ private static boolean awaitCountDownLatches(CountDownLatch[] latches, long timeoutMs)
+ throws InterruptedException {
+ long start = SystemClock.elapsedRealtime();
+ for (int i = 0; i < latches.length; i++) {
+ long timeLeft = timeoutMs - (SystemClock.elapsedRealtime() - start);
+ if (!latches[i].await(timeLeft, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
static class HalEventsGenerator {
private final IVehicle mVehicle;