diff options
author | Pavel Maltsev <pavelm@google.com> | 2017-04-20 13:05:26 -0700 |
---|---|---|
committer | Pavel Maltsev <pavelm@google.com> | 2017-04-20 19:33:41 -0700 |
commit | 45e59a01ae0d00694743b49b6e3135ad97557e6f (patch) | |
tree | d6d6cca63d7a3e7edb456efb9ebc6fdd61f5f2fc /tests/vehiclehal_test | |
parent | 54558e083dcbb0a7a37604793ffe057fb8de13eb (diff) | |
download | Car-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.java | 218 |
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; |