aboutsummaryrefslogtreecommitdiff
path: root/tests/vehiclehal_test
diff options
context:
space:
mode:
authorChao Yan <aceyansf@google.com>2018-05-11 12:16:14 -0700
committerChao Yan <aceyansf@google.com>2018-05-15 15:39:41 -0700
commitf2c109c14bfb74d209a8bd070b873d2cfe2ab0a4 (patch)
tree80c54fd0de3d3357ac834483a58b1c5a89d0aece /tests/vehiclehal_test
parentfda603e310e55d5e7b8abb80e6ee1b2cb9e1e85e (diff)
downloadCar-f2c109c14bfb74d209a8bd070b873d2cfe2ab0a4.tar.gz
Added JSON-based E2E Vhal test framework
Checklist: 1. Fake data generator interface using VHAL generator command 2. VHAL JSON data parser 3. VHAL data verifier 4. E2e test base class with VHAL connection and utility methods 5. Sample HVAC E2E test with test data in assets/ Bug: 76017041 Test: lunch bat_land-userdebug & atest VehicleHALTest Change-Id: I5309a22a4ae3e1c115f6ea7ee0a0da46c73b7d86
Diffstat (limited to 'tests/vehiclehal_test')
-rw-r--r--tests/vehiclehal_test/AndroidManifest.xml2
-rw-r--r--tests/vehiclehal_test/assets/car_hvac_test.json518
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java115
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2eCarTestBase.java137
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java205
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/JsonVhalEventGenerator.java64
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/LinearVhalEventGenerator.java97
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java64
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventGenerator.java58
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventVerifier.java95
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalJsonReader.java95
-rw-r--r--tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalPropMaps.java73
12 files changed, 1338 insertions, 185 deletions
diff --git a/tests/vehiclehal_test/AndroidManifest.xml b/tests/vehiclehal_test/AndroidManifest.xml
index 765d09993c..c03406dba7 100644
--- a/tests/vehiclehal_test/AndroidManifest.xml
+++ b/tests/vehiclehal_test/AndroidManifest.xml
@@ -18,6 +18,8 @@
package="com.android.car.vehiclehal.test"
android:sharedUserId="android.uid.system" >
+ <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
+
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.car.vehiclehal.test"
android:label="Tests for Vehicle HAL APIs"/>
diff --git a/tests/vehiclehal_test/assets/car_hvac_test.json b/tests/vehiclehal_test/assets/car_hvac_test.json
new file mode 100644
index 0000000000..7f6c0bb1a6
--- /dev/null
+++ b/tests/vehiclehal_test/assets/car_hvac_test.json
@@ -0,0 +1,518 @@
+[
+ {
+ "timestamp": 1526063903356950016,
+ "areaId": 1,
+ "value": 1,
+ "prop": 354419984
+ },
+ {
+ "timestamp": 1526063903357100032,
+ "areaId": 4,
+ "value": 1,
+ "prop": 354419984
+ },
+ {
+ "timestamp": 1526063903757636096,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063904959113984,
+ "areaId": 0,
+ "value": 28,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063905159528960,
+ "areaId": 0,
+ "value": 27,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063905359936000,
+ "areaId": 0,
+ "value": 26,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063905560376832,
+ "areaId": 0,
+ "value": 25,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063905760837120,
+ "areaId": 0,
+ "value": 24,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063905961300992,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063906362006016,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063906562436096,
+ "areaId": 0,
+ "value": 2,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063906762857984,
+ "areaId": 0,
+ "value": 3,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063906963272960,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063908364721920,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063910066729984,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063911268203008,
+ "areaId": 0,
+ "value": 22,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063911468478976,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063911668872192,
+ "areaId": 0,
+ "value": 24,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063911869281024,
+ "areaId": 0,
+ "value": 25,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063912069678080,
+ "areaId": 0,
+ "value": 26,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063912270088960,
+ "areaId": 0,
+ "value": 27,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063912670825984,
+ "areaId": 0,
+ "value": 5,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063912871236096,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063913071654912,
+ "areaId": 0,
+ "value": 3,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063913272064000,
+ "areaId": 0,
+ "value": 2,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063914373497856,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063915574942976,
+ "areaId": 0,
+ "value": 28,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063915775356928,
+ "areaId": 0,
+ "value": 27,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063915975784960,
+ "areaId": 0,
+ "value": 26,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063916176208128,
+ "areaId": 0,
+ "value": 25,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063916376483840,
+ "areaId": 0,
+ "value": 24,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063916576890880,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063916977551872,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063917177978112,
+ "areaId": 0,
+ "value": 2,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063917378403072,
+ "areaId": 0,
+ "value": 3,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063917578809856,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063918980086016,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063920681338112,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063921882802944,
+ "areaId": 0,
+ "value": 22,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063922083273984,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063922283792896,
+ "areaId": 0,
+ "value": 24,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063922484265984,
+ "areaId": 0,
+ "value": 25,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063922684783872,
+ "areaId": 0,
+ "value": 26,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063922885256960,
+ "areaId": 0,
+ "value": 27,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063923285954048,
+ "areaId": 0,
+ "value": 5,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063923486427136,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063923686938880,
+ "areaId": 0,
+ "value": 3,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063923887389952,
+ "areaId": 0,
+ "value": 2,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063924988778240,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063926190287104,
+ "areaId": 0,
+ "value": 28,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063926390775040,
+ "areaId": 0,
+ "value": 27,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063926591278080,
+ "areaId": 0,
+ "value": 26,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063926791796992,
+ "areaId": 0,
+ "value": 25,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063926992291840,
+ "areaId": 0,
+ "value": 24,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063927192840960,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063927593507072,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063927793946112,
+ "areaId": 0,
+ "value": 2,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063927994479872,
+ "areaId": 0,
+ "value": 3,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063928194946048,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063929596730112,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063931298659072,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063932500180992,
+ "areaId": 0,
+ "value": 22,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063932700491008,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063932900928000,
+ "areaId": 0,
+ "value": 24,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063933101340928,
+ "areaId": 0,
+ "value": 25,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063933301820160,
+ "areaId": 0,
+ "value": 26,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063933502290944,
+ "areaId": 0,
+ "value": 27,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063933903042048,
+ "areaId": 0,
+ "value": 5,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063934103492864,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063934303913984,
+ "areaId": 0,
+ "value": 3,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063934504412928,
+ "areaId": 0,
+ "value": 2,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063935606041856,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063936807610880,
+ "areaId": 0,
+ "value": 28,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063937008130048,
+ "areaId": 0,
+ "value": 27,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063937208636160,
+ "areaId": 0,
+ "value": 26,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063937409096960,
+ "areaId": 0,
+ "value": 25,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063937609554176,
+ "areaId": 0,
+ "value": 24,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063937810017024,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063938210696960,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063938411200000,
+ "areaId": 0,
+ "value": 2,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063938611734016,
+ "areaId": 0,
+ "value": 3,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063938812249856,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517120
+ },
+ {
+ "timestamp": 1526063940214057984,
+ "areaId": 0,
+ "value": 1,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063941916071936,
+ "areaId": 0,
+ "value": 4,
+ "prop": 356517121
+ },
+ {
+ "timestamp": 1526063943123698944,
+ "areaId": 0,
+ "value": 22,
+ "prop": 358614275
+ },
+ {
+ "timestamp": 1526063943323981056,
+ "areaId": 0,
+ "value": 23,
+ "prop": 358614275
+ }
+] \ No newline at end of file
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java
new file mode 100644
index 0000000000..3b12d8751c
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/CarHvacTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import static org.junit.Assert.assertTrue;
+
+import static java.lang.Integer.toHexString;
+
+import android.car.Car;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.hvac.CarHvacManager;
+import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+import android.util.Log;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.time.Duration;
+import java.util.Set;
+
+
+/**
+ * The test suite will execute end-to-end Car HVAC API test by generating HVAC property data from
+ * default VHAL and verify those data on the fly. The test data is coming from assets/ folder in the
+ * test APK and will be shared with VHAL to execute the test.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class CarHvacTest extends E2eCarTestBase {
+ private static final String TAG = Utils.concatTag(CarHvacTest.class);
+
+ // Test should be completed within 1 hour as it only covers a finite set of HVAC properties
+ private static final Duration TEST_TIME_OUT = Duration.ofHours(1);
+
+ private static final String CAR_HVAC_TEST_JSON = "car_hvac_test.json";
+
+ // Referred to hardware/interfaces/automotive/vehicle/2.0/types.hal, some of the HVAC properties
+ // are in CONTINUOUS mode. They should be omitted when testing ON_CHANGE properties.
+ private static final Set<Integer> CONTINUOUS_HVAC_PROPS;
+
+ static {
+ CONTINUOUS_HVAC_PROPS = new ArraySet<>();
+ CONTINUOUS_HVAC_PROPS.add(VehicleProperty.ENV_OUTSIDE_TEMPERATURE);
+ }
+
+
+ private class CarHvacOnChangeEventListener implements CarHvacEventCallback {
+ private VhalEventVerifier mVerifier;
+
+ CarHvacOnChangeEventListener(VhalEventVerifier verifier) {
+ mVerifier = verifier;
+ }
+
+ @Override
+ public void onChangeEvent(CarPropertyValue carPropertyValue) {
+ VehiclePropValue event = Utils.fromHvacPropertyValue(carPropertyValue);
+ if (!CONTINUOUS_HVAC_PROPS.contains(event.prop)) {
+ mVerifier.verify(Utils.fromHvacPropertyValue(carPropertyValue));
+ }
+ }
+
+ @Override
+ public void onErrorEvent(final int propertyId, final int zone) {
+ Assert.fail("Error: propertyId=" + toHexString(propertyId) + " zone=" + zone);
+ }
+ }
+
+ @Test
+ public void testHvacOperations() throws Exception {
+ Log.d(TAG, "Prepare HVAC test data");
+ VhalEventVerifier verifier = new VhalEventVerifier(getExpectedEvents(CAR_HVAC_TEST_JSON));
+ File sharedJson = makeShareable(CAR_HVAC_TEST_JSON);
+
+ Log.d(TAG, "Start listening to the HAL");
+ CarHvacManager hvacMgr = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+ CarHvacEventCallback callback = new CarHvacOnChangeEventListener(verifier);
+ hvacMgr.registerCallback(callback);
+
+ Log.d(TAG, "Send command to VHAL to start generation");
+ VhalEventGenerator hvacGenerator =
+ new JsonVhalEventGenerator(mVehicle).setJsonFile(sharedJson);
+ hvacGenerator.start();
+
+ Log.d(TAG, "Receiving and verifying VHAL events");
+ verifier.waitForEnd(TEST_TIME_OUT.toMillis());
+
+ Log.d(TAG, "Send command to VHAL to stop generation");
+ hvacGenerator.stop();
+
+ hvacMgr.unregisterCallback(callback);
+
+ assertTrue("Detected mismatched events: " + verifier.getResultString(),
+ verifier.getMismatchedEvents().isEmpty());
+ }
+}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2eCarTestBase.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2eCarTestBase.java
new file mode 100644
index 0000000000..5960422bb2
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2eCarTestBase.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.car.Car;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.hardware.automotive.vehicle.V2_0.IVehicle;
+import android.hardware.automotive.vehicle.V2_0.StatusCode;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.os.ConditionVariable;
+import android.os.FileUtils;
+import android.os.IBinder;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import com.google.android.collect.Lists;
+
+import org.json.JSONException;
+import org.junit.After;
+import org.junit.Before;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+
+public class E2eCarTestBase {
+ private static final String TAG = Utils.concatTag(E2eCarTestBase.class);
+ private static final int DEFAULT_WAIT_TIMEOUT_MS = 1000;
+
+ protected IVehicle mVehicle;
+ protected Car mCar;
+ protected Context mContext;
+ private final CarConnectionListener mConnectionListener = new CarConnectionListener();
+
+ @Before
+ public void connectToVehicleHal() throws Exception {
+ mVehicle = Utils.getVehicle();
+ mVehicle.getPropConfigs(
+ Lists.newArrayList(VhalEventGenerator.GENERATE_FAKE_DATA_CONTROLLING_PROPERTY),
+ (status, propConfigs) -> assumeTrue(status == StatusCode.OK));
+ }
+
+ @Before
+ public void connectToCarService() {
+ mContext = InstrumentationRegistry.getContext();
+ mCar = Car.createCar(mContext, mConnectionListener);
+ assertNotNull(mCar);
+ mCar.connect();
+ mConnectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ }
+
+ @After
+ public void disconnect() {
+ if (mVehicle != null) {
+ mVehicle = null;
+ }
+ if (mCar != null) {
+ mCar.disconnect();
+ mCar = null;
+ }
+ }
+
+ protected List<VehiclePropValue> getExpectedEvents(String fileName)
+ throws IOException, JSONException {
+ try (InputStream in = mContext.getAssets().open(fileName)) {
+ Log.d(TAG, "Reading golden test data" + fileName);
+ return VhalJsonReader.readFromJson(in);
+ }
+ }
+
+ /**
+ * The method copies the test data from assets/ to internal storage and make it publicly
+ * readable, so that default VHAL can access and read the test data.
+ */
+ protected File makeShareable(String fileName) throws IOException {
+ File filesDir = mContext.getFilesDir();
+ // Set publicly executable permission to make sure app internal storage:
+ // /data/user/0/<package> is accessible for default VHAL service
+ if (!filesDir.getParentFile().setExecutable(true, false)) {
+ Log.w(TAG, "Failed to set parent directory +x permission"
+ + filesDir.getParentFile().getAbsolutePath());
+ }
+ File internalFile = new File(filesDir, fileName);
+
+ try (
+ InputStream in = mContext.getAssets().open(fileName);
+ OutputStream out = new FileOutputStream(internalFile)
+ ) {
+ Log.d(TAG, "Copying golden test data to " + internalFile.getAbsolutePath());
+ FileUtils.copy(in, out);
+ }
+ // Make sure the copied test file is publicly readable for default VHAL service. This
+ // operation is risky with security holes and should only be used for testing scenarios.
+ if (!internalFile.setReadable(true, false)) {
+ Log.w(TAG, "Failed to set read permission for " + internalFile.getAbsolutePath());
+ }
+ return internalFile;
+ }
+
+ private static class CarConnectionListener implements ServiceConnection {
+ private final ConditionVariable mConnectionWait = new ConditionVariable();
+
+ void waitForConnection(long timeoutMs) {
+ mConnectionWait.block(timeoutMs);
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mConnectionWait.open();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {}
+ }
+}
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 da6e74a9b9..6427a07233 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
@@ -20,7 +20,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
import static java.lang.Integer.toHexString;
@@ -31,43 +30,22 @@ 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;
-import android.hardware.automotive.vehicle.V2_0.IVehicle;
-import android.hardware.automotive.vehicle.V2_0.StatusCode;
-import android.hardware.automotive.vehicle.V2_0.VehicleArea;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
-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.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import com.android.car.vehiclehal.VehiclePropValueBuilder;
-
import com.google.android.collect.Lists;
-
-import org.junit.After;
-import org.junit.Before;
-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;
/**
@@ -80,79 +58,9 @@ import java.util.concurrent.TimeUnit;
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
-public class E2ePerformanceTest {
+public class E2ePerformanceTest extends E2eCarTestBase {
private static String TAG = Utils.concatTag(E2ePerformanceTest.class);
- private IVehicle mVehicle;
- private final CarConnectionListener mConnectionListener = new CarConnectionListener();
- private Context mContext;
- private Car mCar;
- private HalEventsGenerator mEventsGenerator;
-
- private static Handler sEventHandler;
- private static final HandlerThread sHandlerThread = new HandlerThread(TAG);
-
- private static final int DEFAULT_WAIT_TIMEOUT_MS = 1000;
-
- /**
- * The following property and the two command bits indicate VHAL to start/stop linear fake data
- * generation process. It must match kGenerateFakeDataControllingProperty that is defined in
- * default VHAL implementation:
- *
- * hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
- *
- * TODO: Move the following definition to a common place that can be shared within package
- */
- private static final int GENERATE_FAKE_DATA_CONTROLLING_PROPERTY = 0x0666
- | VehiclePropertyGroup.VENDOR
- | VehicleArea.GLOBAL
- | VehiclePropertyType.MIXED;
-
- /**
- * The two command bits are sent via GENERATE_FAKE_DATA_CONTROLLING_PROPERTY to start/stop
- * linear fake data generation from VHAL
- */
- private static final int CMD_START_LINEAR = 0;
- private static final int CMD_STOP_LINEAR = 1;
-
- @BeforeClass
- public static void setupEventHandler() {
- sHandlerThread.start();
- sEventHandler = new Handler(sHandlerThread.getLooper());
- }
-
- @Before
- public void connectToVehicleHal() throws Exception {
- mVehicle = Utils.getVehicle();
-
- mVehicle.getPropConfigs(Lists.newArrayList(GENERATE_FAKE_DATA_CONTROLLING_PROPERTY),
- (status, propConfigs) -> assumeTrue(status == StatusCode.OK));
-
- mEventsGenerator = new HalEventsGenerator(mVehicle);
- }
-
- @Before
- public void connectToCarService() throws Exception {
- mContext = InstrumentationRegistry.getContext();
- mCar = Car.createCar(mContext, mConnectionListener, sEventHandler);
- assertNotNull(mCar);
- mCar.connect();
- mConnectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
- }
-
- @After
- public void disconnect() throws Exception {
- if (mVehicle != null) {
- mEventsGenerator.stop();
- mVehicle = null;
- mEventsGenerator = null;
- }
- if (mCar != null) {
- mCar.disconnect();
- mCar = null;
- }
- }
-
@Test
public void singleOnChangeProperty() throws Exception {
verifyEventsFromSingleProperty(
@@ -183,20 +91,23 @@ public class E2ePerformanceTest {
mgr.isSensorSupported(mgrPropId));
}
- mEventsGenerator
- .reset()
+ VhalEventGenerator odometerGenerator = new LinearVhalEventGenerator(mVehicle)
+ .setProp(VehicleProperty.PERF_ODOMETER)
.setIntervalMs(EVENT_INTERVAL_MS)
.setInitialValue(1000)
.setIncrement(1.0f)
- .setDispersion(100)
- .start(VehicleProperty.PERF_ODOMETER);
+ .setDispersion(100);
+
- mEventsGenerator
+ VhalEventGenerator speedGenerator = new LinearVhalEventGenerator(mVehicle)
+ .setProp(VehicleProperty.PERF_VEHICLE_SPEED)
.setIntervalMs(EVENT_INTERVAL_MS)
.setInitialValue(20.0f)
.setIncrement(0.1f)
- .setDispersion(10)
- .start(VehicleProperty.PERF_VEHICLE_SPEED);
+ .setDispersion(10);
+
+ odometerGenerator.start();
+ speedGenerator.start();
SparseArray<CountDownLatch> eventsCounters = new SparseArray<>();
for (int i = 0; i < mgrProperties.length; i++) {
@@ -214,7 +125,9 @@ public class E2ePerformanceTest {
}
boolean allEventsReceived = awaitCountDownLatches(latches, WAIT_TIME);
mgr.unregisterListener(listener);
- mEventsGenerator.stop();
+
+ odometerGenerator.stop();
+ speedGenerator.stop();
if (!allEventsReceived) {
SparseIntArray missingEventsPerProperty = new SparseIntArray();
@@ -359,12 +272,14 @@ public class E2ePerformanceTest {
assertNotNull(mgr);
assertTrue(mgr.isSensorSupported(mgrPropId));
- mEventsGenerator
+ VhalEventGenerator generator = new LinearVhalEventGenerator(mVehicle)
+ .setProp(halPropId)
.setIntervalMs(10)
.setInitialValue(INITIAL_VALUE)
.setIncrement(INCREMENT)
- .setDispersion(100)
- .start(halPropId);
+ .setDispersion(100);
+
+ generator.start();
CountDownLatch latch = new CountDownLatch(EXPECTED_EVENTS);
OnSensorChangedListener listener = event -> latch.countDown();
@@ -373,25 +288,11 @@ public class E2ePerformanceTest {
try {
assertTrue(latch.await(EXPECTED_TIME_DURATION_MS, TimeUnit.MILLISECONDS));
} finally {
+ generator.stop();
mgr.unregisterListener(listener);
}
}
- private static class CarConnectionListener implements ServiceConnection {
- private final Semaphore mConnectionWait = new Semaphore(0);
-
- void waitForConnection(long timeoutMs) throws InterruptedException {
- mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mConnectionWait.release();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) { }
- }
private static boolean awaitCountDownLatches(CountDownLatch[] latches, long timeoutMs)
throws InterruptedException {
@@ -405,68 +306,4 @@ public class E2ePerformanceTest {
return true;
}
-
- static class HalEventsGenerator {
- private final IVehicle mVehicle;
-
- private long mIntervalMs;
- private float mInitialValue;
- private float mDispersion;
- private float mIncrement;
-
- HalEventsGenerator(IVehicle vehicle) {
- mVehicle = vehicle;
- reset();
- }
-
- HalEventsGenerator reset() {
- mIntervalMs = 1000;
- mInitialValue = 1000;
- mDispersion = 0;
- mInitialValue = 0;
- return this;
- }
-
- HalEventsGenerator setIntervalMs(long intervalMs) {
- mIntervalMs = intervalMs;
- return this;
- }
-
- HalEventsGenerator setInitialValue(float initialValue) {
- mInitialValue = initialValue;
- return this;
- }
-
- HalEventsGenerator setDispersion(float dispersion) {
- mDispersion = dispersion;
- return this;
- }
-
- HalEventsGenerator setIncrement(float increment) {
- mIncrement = increment;
- return this;
- }
-
- void start(int propId) throws RemoteException {
- VehiclePropValue request =
- VehiclePropValueBuilder.newBuilder(GENERATE_FAKE_DATA_CONTROLLING_PROPERTY)
- .addIntValue(CMD_START_LINEAR, propId)
- .setInt64Value(mIntervalMs * 1000_000)
- .addFloatValue(mInitialValue, mDispersion, mIncrement)
- .build();
- assertEquals(StatusCode.OK, mVehicle.set(request));
- }
-
- void stop() throws RemoteException {
- stop(0);
- }
-
- void stop(int propId) throws RemoteException {
- VehiclePropValue request =
- VehiclePropValueBuilder.newBuilder(GENERATE_FAKE_DATA_CONTROLLING_PROPERTY)
- .addIntValue(CMD_STOP_LINEAR, propId)
- .build();
- assertEquals(StatusCode.OK, mVehicle.set(request));
- }
- }
}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/JsonVhalEventGenerator.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/JsonVhalEventGenerator.java
new file mode 100644
index 0000000000..702f5f0a5a
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/JsonVhalEventGenerator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.automotive.vehicle.V2_0.IVehicle;
+import android.hardware.automotive.vehicle.V2_0.StatusCode;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.os.RemoteException;
+
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+
+import java.io.File;
+
+class JsonVhalEventGenerator implements VhalEventGenerator {
+
+ private IVehicle mVehicle;
+ private File mFile;
+
+ JsonVhalEventGenerator(IVehicle vehicle) {
+ mVehicle = vehicle;
+ }
+
+ public JsonVhalEventGenerator setJsonFile(File file) throws Exception {
+ if (!file.exists()) {
+ throw new Exception("JSON test data file does not exist: " + file.getAbsolutePath());
+ }
+ mFile = file;
+ return this;
+ }
+
+ @Override
+ public void start() throws RemoteException {
+ VehiclePropValue request =
+ VehiclePropValueBuilder.newBuilder(GENERATE_FAKE_DATA_CONTROLLING_PROPERTY)
+ .addIntValue(CMD_START_JSON)
+ .setStringValue(mFile.getAbsolutePath())
+ .build();
+ assertEquals(StatusCode.OK, mVehicle.set(request));
+ }
+
+ @Override
+ public void stop() throws RemoteException {
+ VehiclePropValue request =
+ VehiclePropValueBuilder.newBuilder(GENERATE_FAKE_DATA_CONTROLLING_PROPERTY)
+ .addIntValue(CMD_STOP_JSON)
+ .build();
+ assertEquals(StatusCode.OK, mVehicle.set(request));
+ }
+}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/LinearVhalEventGenerator.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/LinearVhalEventGenerator.java
new file mode 100644
index 0000000000..856e1fc90e
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/LinearVhalEventGenerator.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.automotive.vehicle.V2_0.IVehicle;
+import android.hardware.automotive.vehicle.V2_0.StatusCode;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.os.RemoteException;
+
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+
+import java.time.Duration;
+
+class LinearVhalEventGenerator implements VhalEventGenerator {
+
+ private final IVehicle mVehicle;
+
+ private int mProp;
+ private Duration mInterval;
+ private float mInitialValue;
+ private float mDispersion;
+ private float mIncrement;
+
+ LinearVhalEventGenerator(IVehicle vehicle) {
+ mVehicle = vehicle;
+ reset();
+ }
+
+ LinearVhalEventGenerator reset() {
+ mProp = 0;
+ mInterval = Duration.ofSeconds(1);
+ mInitialValue = 1000;
+ mDispersion = 0;
+ mInitialValue = 0;
+ return this;
+ }
+
+ LinearVhalEventGenerator setIntervalMs(long intervalMs) {
+ mInterval = Duration.ofMillis(intervalMs);
+ return this;
+ }
+
+ LinearVhalEventGenerator setInitialValue(float initialValue) {
+ mInitialValue = initialValue;
+ return this;
+ }
+
+ LinearVhalEventGenerator setDispersion(float dispersion) {
+ mDispersion = dispersion;
+ return this;
+ }
+
+ LinearVhalEventGenerator setIncrement(float increment) {
+ mIncrement = increment;
+ return this;
+ }
+
+ LinearVhalEventGenerator setProp(int prop) {
+ mProp = prop;
+ return this;
+ }
+
+ @Override
+ public void start() throws RemoteException {
+ VehiclePropValue request =
+ VehiclePropValueBuilder.newBuilder(GENERATE_FAKE_DATA_CONTROLLING_PROPERTY)
+ .addIntValue(CMD_START_LINEAR, mProp)
+ .setInt64Value(mInterval.toNanos())
+ .addFloatValue(mInitialValue, mDispersion, mIncrement)
+ .build();
+ assertEquals(StatusCode.OK, mVehicle.set(request));
+ }
+
+ @Override
+ public void stop() throws RemoteException {
+ VehiclePropValue request =
+ VehiclePropValueBuilder.newBuilder(GENERATE_FAKE_DATA_CONTROLLING_PROPERTY)
+ .addIntValue(CMD_STOP_LINEAR, mProp)
+ .build();
+ assertEquals(StatusCode.OK, mVehicle.set(request));
+ }
+}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
index 7425973d31..6a4b49689f 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
@@ -16,10 +16,13 @@
package com.android.car.vehiclehal.test;
-import android.annotation.Nullable;
+import static java.lang.Integer.toHexString;
+
+import android.car.hardware.CarPropertyValue;
import android.hardware.automotive.vehicle.V2_0.IVehicle;
import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.os.HidlSupport;
import android.os.RemoteException;
import android.util.Log;
@@ -91,4 +94,63 @@ final class Utils {
Log.d(TAG, "Connected to IVehicle service: " + service);
return service;
}
+
+ /**
+ * Check the equality of two VehiclePropValue object ignoring timestamp and status.
+ *
+ * @param value1
+ * @param value2
+ * @return true if equal
+ */
+ static boolean areVehiclePropValuesEqual(final VehiclePropValue value1,
+ final VehiclePropValue value2) {
+ return value1 == value2
+ || value1 != null
+ && value2 != null
+ && value1.prop == value2.prop
+ && value1.areaId == value2.areaId
+ && HidlSupport.deepEquals(value1.value, value2.value);
+ }
+
+ /**
+ * The method will convert prop ID to hexadecimal format, and omit timestamp and status
+ *
+ * @param value
+ * @return String
+ */
+ static String vehiclePropValueToString(final VehiclePropValue value) {
+ return "{.prop = 0x" + toHexString(value.prop)
+ + ", .areaId = " + value.areaId
+ + ", .value = " + value.value + "}";
+ }
+
+ static VehiclePropValue fromHvacPropertyValue(CarPropertyValue value) {
+ VehiclePropValueBuilder builder =
+ VehiclePropValueBuilder.newBuilder(
+ VhalPropMaps.getHvacVhalProp(value.getPropertyId()));
+ return fromCarPropertyValue(value, builder);
+ }
+
+ private static VehiclePropValue fromCarPropertyValue(
+ CarPropertyValue value, VehiclePropValueBuilder builder) {
+ builder.setAreaId(value.getAreaId()).setTimestamp(value.getTimestamp());
+
+ //TODO: Consider move this conversion to VehiclePropValueBuilder
+ Object o = value.getValue();
+ if (o instanceof Boolean) {
+ builder.addIntValue((boolean) o ? 1 : 0);
+ } else if (o instanceof Integer) {
+ builder.addIntValue((int) o);
+ } else if (o instanceof Float) {
+ builder.addFloatValue((float) o);
+ } else if (o instanceof Long) {
+ builder.setInt64Value((long) o);
+ } else if (o instanceof String) {
+ builder.setStringValue((String) o);
+ } else { //TODO: Add support for MIXED type
+ throw new IllegalArgumentException("Unrecognized car property value type, "
+ + o.getClass());
+ }
+ return builder.build();
+ }
}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventGenerator.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventGenerator.java
new file mode 100644
index 0000000000..b71bf6c236
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import android.hardware.automotive.vehicle.V2_0.VehicleArea;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
+import android.os.RemoteException;
+
+interface VhalEventGenerator {
+
+ /**
+ * The following property controls VHAL to start/stop linear fake data generation process.
+ * It must match kGenerateFakeDataControllingProperty that is defined in default VHAL
+ * implementation:
+ *
+ * hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+ */
+ int GENERATE_FAKE_DATA_CONTROLLING_PROPERTY = 0x0666
+ | VehiclePropertyGroup.VENDOR
+ | VehicleArea.GLOBAL
+ | VehiclePropertyType.MIXED;
+
+ // Command bits sent via GENERATE_FAKE_DATA_CONTROLLING_PROPERTY to control fake data generation
+ int CMD_START_LINEAR = 0; // Start linear fake data generation
+ int CMD_STOP_LINEAR = 1; // Stop linear fake data generation
+ int CMD_START_JSON = 2; // Start JSON-based fake data generation
+ int CMD_STOP_JSON = 3; // Stop JSON-based fake data generation
+
+ /**
+ * Asynchronous call to tell VHAL to start fake event generation. VHAL will start generating
+ * data after this call
+ *
+ * @throws RemoteException
+ */
+ void start() throws RemoteException;
+
+ /**
+ * Synchronous call to tell VHAL to stop fake event generation. VHAL should always stopped
+ * generating data after this call.
+ *
+ * @throws RemoteException
+ */
+ void stop() throws RemoteException;
+}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventVerifier.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventVerifier.java
new file mode 100644
index 0000000000..5ac92cae77
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalEventVerifier.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.os.ConditionVariable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The verifier class is used to verify received VHAL events against expected events on-the-fly.
+ * It is initialized with a list of expected events and moving down the list to verify received
+ * events. The verifier object not reusable and should be discarded once the verification is done.
+ * The verifier will provide formatted result for all mismatched events in sequence.
+ */
+class VhalEventVerifier {
+ private List<VehiclePropValue> mExpectedEvents;
+ // A pointer to keep track of the next expected event in the list
+ private int mIdx;
+ private List<MismatchedEventPair> mMismatchedEvents;
+ // Condition variable to notify waiting threads when verification is done or timeout.
+ private ConditionVariable mCond;
+
+ static class MismatchedEventPair {
+ public final int idx;
+ public final VehiclePropValue expectedEvent;
+ public final VehiclePropValue mismatchedEvent;
+
+ MismatchedEventPair(VehiclePropValue expectedEvent, VehiclePropValue mismatchedEvent,
+ int idx) {
+ this.idx = idx;
+ this.expectedEvent = expectedEvent;
+ this.mismatchedEvent = mismatchedEvent;
+ }
+ }
+
+ VhalEventVerifier(List<VehiclePropValue> expectedEvents) {
+ mExpectedEvents = expectedEvents;
+ mIdx = 0;
+ mMismatchedEvents = new ArrayList<>();
+ mCond = new ConditionVariable(expectedEvents.isEmpty());
+ }
+
+ /**
+ * Verification method that checks the equality of received event against expected event. Once
+ * it reaches to the end of list, it will unblock the waiting threads. Note, the verification
+ * method is not thread-safe. It assumes only a single thread is calling the method at all time.
+ *
+ * @param nextEvent to be verified
+ */
+ public void verify(VehiclePropValue nextEvent) {
+ if (mIdx >= mExpectedEvents.size()) {
+ return;
+ }
+ VehiclePropValue expectedEvent = mExpectedEvents.get(mIdx);
+ if (!Utils.areVehiclePropValuesEqual(expectedEvent, nextEvent)) {
+ mMismatchedEvents.add(new MismatchedEventPair(expectedEvent, nextEvent, mIdx));
+ }
+ if (++mIdx == mExpectedEvents.size()) {
+ mCond.open();
+ }
+ }
+
+ public List<MismatchedEventPair> getMismatchedEvents() {
+ return mMismatchedEvents;
+ }
+
+ public void waitForEnd(long timeout) {
+ mCond.block(timeout);
+ }
+
+ public String getResultString() {
+ StringBuilder resultBuilder = new StringBuilder();
+ for (MismatchedEventPair pair : mMismatchedEvents) {
+ resultBuilder.append("Index " + pair.idx + ": Expected "
+ + Utils.vehiclePropValueToString(pair.expectedEvent) + ", Received "
+ + Utils.vehiclePropValueToString(pair.mismatchedEvent) + "\n");
+ }
+ return resultBuilder.toString();
+ }
+}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalJsonReader.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalJsonReader.java
new file mode 100644
index 0000000000..bf4e32fa59
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalJsonReader.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import static java.lang.Integer.toHexString;
+
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
+
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+
+class VhalJsonReader {
+
+ /**
+ * Name of fields presented in JSON file. All of them are required.
+ */
+ private static final String JSON_FIELD_PROP = "prop";
+ private static final String JSON_FIELD_AREA_ID = "areaId";
+ private static final String JSON_FIELD_TIMESTAMP = "timestamp";
+ private static final String JSON_FIELD_VALUE = "value";
+
+ public static List<VehiclePropValue> readFromJson(InputStream in)
+ throws IOException, JSONException {
+ JSONArray rawEvents = new JSONArray(readJsonString(in));
+ List<VehiclePropValue> events = new ArrayList<>();
+ for (int i = 0; i < rawEvents.length(); i++) {
+ events.add(getEvent(rawEvents.getJSONObject(i)));
+ }
+ return events;
+ }
+
+ private static String readJsonString(InputStream in) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(in, StandardCharsets.UTF_8))) {
+ reader.lines().forEach(builder::append);
+ }
+ return builder.toString();
+ }
+
+ private static VehiclePropValue getEvent(JSONObject rawEvent) throws JSONException {
+ int prop = rawEvent.getInt(JSON_FIELD_PROP);
+ VehiclePropValueBuilder builder = VehiclePropValueBuilder.newBuilder(prop)
+ .setAreaId(rawEvent.getInt(JSON_FIELD_AREA_ID))
+ .setTimestamp(rawEvent.getLong(JSON_FIELD_TIMESTAMP));
+
+ switch (prop & VehiclePropertyType.MASK) {
+ case VehiclePropertyType.BOOLEAN:
+ case VehiclePropertyType.INT32:
+ builder.addIntValue(rawEvent.getInt(JSON_FIELD_VALUE));
+ break;
+ case VehiclePropertyType.INT64:
+ builder.setInt64Value(rawEvent.getLong(JSON_FIELD_VALUE));
+ break;
+ case VehiclePropertyType.FLOAT:
+ builder.addFloatValue((float) rawEvent.getDouble(JSON_FIELD_VALUE));
+ break;
+ case VehiclePropertyType.STRING:
+ builder.setStringValue(rawEvent.getString(JSON_FIELD_VALUE));
+ break;
+ //TODO: Add VehiclePropertyType.MIXED type support
+ default:
+ throw new IllegalArgumentException("Property type 0x"
+ + toHexString(prop & VehiclePropertyType.MASK)
+ + " is not supported in the test.");
+ }
+ return builder.build();
+ }
+}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalPropMaps.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalPropMaps.java
new file mode 100644
index 0000000000..f24cad68d8
--- /dev/null
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/VhalPropMaps.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.vehiclehal.test;
+
+import android.car.hardware.hvac.CarHvacManager;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.util.SparseIntArray;
+
+class VhalPropMaps {
+
+ private static final SparseIntArray HVAC_PROP_MAP;
+
+ static {
+ HVAC_PROP_MAP = new SparseIntArray();
+ HVAC_PROP_MAP.put(CarHvacManager.ID_MIRROR_DEFROSTER_ON,
+ VehicleProperty.HVAC_SIDE_MIRROR_HEAT);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_STEERING_WHEEL_HEAT,
+ VehicleProperty.HVAC_STEERING_WHEEL_HEAT);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_OUTSIDE_AIR_TEMP,
+ VehicleProperty.ENV_OUTSIDE_TEMPERATURE);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_TEMPERATURE_DISPLAY_UNITS,
+ VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_TEMP_SETPOINT,
+ VehicleProperty.HVAC_TEMPERATURE_SET);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_TEMP_ACTUAL,
+ VehicleProperty.HVAC_TEMPERATURE_CURRENT);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT,
+ VehicleProperty.HVAC_FAN_SPEED);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_FAN_SPEED_RPM,
+ VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_FAN_DIRECTION_AVAILABLE,
+ VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_FAN_DIRECTION,
+ VehicleProperty.HVAC_FAN_DIRECTION);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_SEAT_TEMP,
+ VehicleProperty.HVAC_SEAT_TEMPERATURE);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_AC_ON,
+ VehicleProperty.HVAC_AC_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON,
+ VehicleProperty.HVAC_AUTO_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,
+ VehicleProperty.HVAC_RECIRC_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_MAX_AC_ON,
+ VehicleProperty.HVAC_MAX_AC_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_DUAL_ZONE_ON,
+ VehicleProperty.HVAC_DUAL_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_MAX_DEFROST_ON,
+ VehicleProperty.HVAC_MAX_DEFROST_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_HVAC_POWER_ON,
+ VehicleProperty.HVAC_POWER_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON,
+ VehicleProperty.HVAC_AUTO_RECIRC_ON);
+ HVAC_PROP_MAP.put(CarHvacManager.ID_WINDOW_DEFROSTER_ON,
+ VehicleProperty.HVAC_DEFROSTER);
+ }
+
+ static int getHvacVhalProp(final int hvacProp) {
+ return HVAC_PROP_MAP.get(hvacProp);
+ }
+}