| /* |
| * Copyright (C) 2021 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. |
| */ |
| |
| #include <FakeVehicleHardware.h> |
| |
| #include <DefaultConfig.h> |
| #include <FakeObd2Frame.h> |
| #include <FakeUserHal.h> |
| #include <PropertyUtils.h> |
| #include <TestPropertyUtils.h> |
| |
| #include <android-base/expected.h> |
| #include <android-base/file.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <utils/Log.h> |
| #include <utils/SystemClock.h> |
| |
| #include <inttypes.h> |
| #include <vector> |
| |
| namespace android { |
| namespace hardware { |
| namespace automotive { |
| namespace vehicle { |
| namespace fake { |
| namespace { |
| |
| using ::aidl::android::hardware::automotive::vehicle::GetValueRequest; |
| using ::aidl::android::hardware::automotive::vehicle::GetValueResult; |
| using ::aidl::android::hardware::automotive::vehicle::RawPropValues; |
| using ::aidl::android::hardware::automotive::vehicle::SetValueRequest; |
| using ::aidl::android::hardware::automotive::vehicle::SetValueResult; |
| using ::aidl::android::hardware::automotive::vehicle::StatusCode; |
| using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport; |
| using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; |
| using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; |
| using ::android::base::expected; |
| using ::android::base::unexpected; |
| using ::testing::ContainerEq; |
| using ::testing::Eq; |
| using ::testing::IsSubsetOf; |
| using ::testing::WhenSortedBy; |
| |
| constexpr int INVALID_PROP_ID = 0; |
| |
| } // namespace |
| |
| // A helper class to access private methods for FakeVehicleHardware. |
| class FakeVehicleHardwareTestHelper { |
| public: |
| FakeVehicleHardwareTestHelper(FakeVehicleHardware* hardware) { mHardware = hardware; } |
| |
| void overrideProperties(const char* overrideDir) { mHardware->overrideProperties(overrideDir); } |
| |
| private: |
| FakeVehicleHardware* mHardware; |
| }; |
| |
| class FakeVehicleHardwareTest : public ::testing::Test { |
| protected: |
| void SetUp() override { |
| auto callback = std::make_unique<IVehicleHardware::PropertyChangeCallback>( |
| [this](const std::vector<VehiclePropValue>& values) { |
| onPropertyChangeEvent(values); |
| }); |
| getHardware()->registerOnPropertyChangeEvent(std::move(callback)); |
| mSetValuesCallback = std::make_shared<IVehicleHardware::SetValuesCallback>( |
| [this](std::vector<SetValueResult> results) { onSetValues(results); }); |
| mGetValuesCallback = std::make_shared<IVehicleHardware::GetValuesCallback>( |
| [this](std::vector<GetValueResult> results) { onGetValues(results); }); |
| } |
| |
| FakeVehicleHardware* getHardware() { return &mHardware; } |
| |
| StatusCode setValues(const std::vector<SetValueRequest>& requests) { |
| return getHardware()->setValues(mSetValuesCallback, requests); |
| } |
| |
| StatusCode getValues(const std::vector<GetValueRequest>& requests) { |
| return getHardware()->getValues(mGetValuesCallback, requests); |
| } |
| |
| StatusCode setValue(const VehiclePropValue& value) { |
| std::vector<SetValueRequest> requests = { |
| SetValueRequest{ |
| .requestId = 0, |
| .value = value, |
| }, |
| }; |
| |
| if (StatusCode status = setValues(requests); status != StatusCode::OK) { |
| return status; |
| } |
| |
| const SetValueResult& result = getSetValueResults().back(); |
| |
| if (result.requestId != 0) { |
| ALOGE("request ID mismatch, got %" PRId64 ", expect 0", result.requestId); |
| return StatusCode::INTERNAL_ERROR; |
| } |
| |
| return result.status; |
| } |
| |
| expected<VehiclePropValue, StatusCode> getValue(const VehiclePropValue& value) { |
| std::vector<GetValueRequest> requests = { |
| GetValueRequest{ |
| .requestId = 0, |
| .prop = value, |
| }, |
| }; |
| |
| if (StatusCode status = getValues(requests); status != StatusCode::OK) { |
| return unexpected(status); |
| } |
| |
| const GetValueResult& result = getGetValueResults().back(); |
| if (result.requestId != 0) { |
| ALOGE("request ID mismatch, got %" PRId64 ", expect 0", result.requestId); |
| return unexpected(StatusCode::INTERNAL_ERROR); |
| } |
| |
| if (result.status != StatusCode::OK) { |
| return unexpected(result.status); |
| } |
| |
| if (!result.prop.has_value()) { |
| ALOGE("%s", "result property is empty"); |
| return unexpected(StatusCode::INTERNAL_ERROR); |
| } |
| |
| return result.prop.value(); |
| } |
| |
| template <class T> |
| int getStatus(expected<T, StatusCode> result) { |
| return toInt(result.error()); |
| } |
| |
| void onSetValues(std::vector<SetValueResult> results) { |
| for (auto& result : results) { |
| mSetValueResults.push_back(result); |
| } |
| } |
| |
| const std::vector<SetValueResult>& getSetValueResults() { return mSetValueResults; } |
| |
| void onGetValues(std::vector<GetValueResult> results) { |
| for (auto& result : results) { |
| mGetValueResults.push_back(result); |
| } |
| } |
| |
| const std::vector<GetValueResult>& getGetValueResults() { return mGetValueResults; } |
| |
| void onPropertyChangeEvent(std::vector<VehiclePropValue> values) { |
| for (auto& value : values) { |
| mChangedProperties.push_back(value); |
| } |
| } |
| |
| const std::vector<VehiclePropValue>& getChangedProperties() { return mChangedProperties; } |
| |
| void clearChangedProperties() { mChangedProperties.clear(); } |
| |
| static void addSetValueRequest(std::vector<SetValueRequest>& requests, |
| std::vector<SetValueResult>& expectedResults, int64_t requestId, |
| const VehiclePropValue& value, StatusCode expectedStatus) { |
| SetValueRequest request; |
| request.requestId = requestId; |
| request.value = value; |
| request.value.timestamp = elapsedRealtimeNano(); |
| requests.push_back(std::move(request)); |
| |
| SetValueResult result; |
| result.requestId = requestId; |
| result.status = expectedStatus; |
| expectedResults.push_back(std::move(result)); |
| } |
| |
| static void addGetValueRequest(std::vector<GetValueRequest>& requests, |
| std::vector<GetValueResult>& expectedResults, int64_t requestId, |
| const VehiclePropValue& value, StatusCode expectedStatus) { |
| GetValueRequest request; |
| request.requestId = requestId; |
| request.prop.prop = value.prop; |
| request.prop.areaId = value.areaId; |
| requests.push_back(std::move(request)); |
| |
| GetValueResult result; |
| result.requestId = requestId; |
| result.status = expectedStatus; |
| if (expectedStatus == StatusCode::OK) { |
| result.prop = value; |
| } |
| expectedResults.push_back(std::move(result)); |
| } |
| |
| std::vector<VehiclePropValue> getTestPropValues() { |
| VehiclePropValue fuelCapacity = { |
| .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), |
| .value = {.floatValues = {1.0}}, |
| }; |
| |
| VehiclePropValue leftTirePressure = { |
| .prop = toInt(VehicleProperty::TIRE_PRESSURE), |
| .value = {.floatValues = {170.0}}, |
| .areaId = WHEEL_FRONT_LEFT, |
| }; |
| |
| VehiclePropValue rightTirePressure = { |
| .prop = toInt(VehicleProperty::TIRE_PRESSURE), |
| .value = {.floatValues = {180.0}}, |
| .areaId = WHEEL_FRONT_RIGHT, |
| }; |
| |
| return {fuelCapacity, leftTirePressure, rightTirePressure}; |
| } |
| |
| struct PropValueCmp { |
| bool operator()(const VehiclePropValue& a, const VehiclePropValue& b) const { |
| return (a.prop < b.prop) || ((a.prop == b.prop) && (a.value < b.value)) || |
| ((a.prop == b.prop) && (a.value == b.value) && (a.areaId < b.areaId)); |
| } |
| } mPropValueCmp; |
| |
| private: |
| FakeVehicleHardware mHardware; |
| std::vector<SetValueResult> mSetValueResults; |
| std::vector<GetValueResult> mGetValueResults; |
| std::vector<VehiclePropValue> mChangedProperties; |
| std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback; |
| std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback; |
| }; |
| |
| TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) { |
| std::vector<VehiclePropConfig> configs = getHardware()->getAllPropertyConfigs(); |
| |
| ASSERT_EQ(configs.size(), defaultconfig::getDefaultConfigs().size()); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) { |
| std::vector<GetValueRequest> getValueRequests; |
| std::vector<GetValueResult> expectedGetValueResults; |
| int64_t requestId = 1; |
| |
| for (auto& config : defaultconfig::getDefaultConfigs()) { |
| if (obd2frame::FakeObd2Frame::isDiagnosticProperty(config.config)) { |
| // Ignore storing default value for diagnostic property. They have special get/set |
| // logic. |
| continue; |
| } |
| |
| if (FakeUserHal::isSupported(config.config.prop)) { |
| // Ignore fake user HAL properties, they have special logic for getting values. |
| continue; |
| } |
| |
| int propId = config.config.prop; |
| if (isGlobalProp(propId)) { |
| if (config.initialValue == RawPropValues{}) { |
| addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, |
| VehiclePropValue{.prop = propId}, StatusCode::NOT_AVAILABLE); |
| continue; |
| } |
| addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, |
| VehiclePropValue{ |
| .prop = propId, |
| .value = config.initialValue, |
| }, |
| StatusCode::OK); |
| continue; |
| } |
| for (auto areaConfig : config.config.areaConfigs) { |
| StatusCode status = StatusCode::OK; |
| VehiclePropValue propValue{ |
| .prop = propId, |
| .areaId = areaConfig.areaId, |
| }; |
| if (config.initialAreaValues.empty()) { |
| if (config.initialValue == RawPropValues{}) { |
| status = StatusCode::NOT_AVAILABLE; |
| } else { |
| propValue.value = config.initialValue; |
| } |
| } else if (auto valueForAreaIt = config.initialAreaValues.find(areaConfig.areaId); |
| valueForAreaIt != config.initialAreaValues.end()) { |
| propValue.value = valueForAreaIt->second; |
| } else { |
| status = StatusCode::NOT_AVAILABLE; |
| } |
| addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, propValue, |
| status); |
| } |
| } |
| |
| // In our implementation, this would finish immediately. |
| StatusCode status = getValues(getValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| std::vector<GetValueResult> getValueResultsWithNoTimestamp; |
| for (auto& result : getGetValueResults()) { |
| GetValueResult resultCopy = result; |
| resultCopy.prop->timestamp = 0; |
| getValueResultsWithNoTimestamp.push_back(std::move(resultCopy)); |
| } |
| ASSERT_THAT(getValueResultsWithNoTimestamp, ContainerEq(expectedGetValueResults)); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testSetValues) { |
| std::vector<SetValueRequest> requests; |
| std::vector<SetValueResult> expectedResults; |
| |
| int64_t requestId = 1; |
| for (auto& value : getTestPropValues()) { |
| addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK); |
| } |
| |
| StatusCode status = setValues(requests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Although callback might be called asynchronously, in our implementation, the callback would |
| // be called before setValues returns. |
| ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults)); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testSetValuesError) { |
| std::vector<SetValueRequest> requests; |
| std::vector<SetValueResult> expectedResults; |
| |
| int64_t requestId = 1; |
| |
| VehiclePropValue invalidProp = { |
| .prop = INVALID_PROP_ID, |
| }; |
| addSetValueRequest(requests, expectedResults, requestId++, invalidProp, |
| StatusCode::INVALID_ARG); |
| |
| for (auto& value : getTestPropValues()) { |
| addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK); |
| } |
| |
| StatusCode status = setValues(requests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Although callback might be called asynchronously, in our implementation, the callback would |
| // be called before setValues returns. |
| ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults)); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testRegisterOnPropertyChangeEvent) { |
| // We have already registered this callback in Setup, here we are registering again. |
| auto callback = std::make_unique<IVehicleHardware::PropertyChangeCallback>( |
| [this](const std::vector<VehiclePropValue>& values) { onPropertyChangeEvent(values); }); |
| getHardware()->registerOnPropertyChangeEvent(std::move(callback)); |
| |
| auto testValues = getTestPropValues(); |
| std::vector<SetValueRequest> requests; |
| std::vector<SetValueResult> expectedResults; |
| int64_t requestId = 1; |
| for (auto& value : testValues) { |
| addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK); |
| } |
| int64_t timestamp = elapsedRealtimeNano(); |
| |
| StatusCode status = setValues(requests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| auto updatedValues = getChangedProperties(); |
| std::vector<VehiclePropValue> updatedValuesWithNoTimestamp; |
| for (auto& value : updatedValues) { |
| ASSERT_GE(value.timestamp, timestamp); |
| VehiclePropValue valueCopy = value; |
| valueCopy.timestamp = 0; |
| updatedValuesWithNoTimestamp.push_back(std::move(valueCopy)); |
| } |
| |
| ASSERT_THAT(updatedValuesWithNoTimestamp, WhenSortedBy(mPropValueCmp, Eq(testValues))); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testReadValues) { |
| std::vector<SetValueRequest> setValueRequests; |
| std::vector<SetValueResult> expectedSetValueResults; |
| |
| int64_t requestId = 1; |
| for (auto& value : getTestPropValues()) { |
| addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, value, |
| StatusCode::OK); |
| } |
| int64_t timestamp = elapsedRealtimeNano(); |
| |
| // In our implementation, this would finish immediately. |
| StatusCode status = setValues(setValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| std::vector<GetValueRequest> getValueRequests; |
| std::vector<GetValueResult> expectedGetValueResults; |
| for (auto& value : getTestPropValues()) { |
| addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, value, |
| StatusCode::OK); |
| } |
| |
| // In our implementation, this would finish immediately. |
| status = getValues(getValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| std::vector<GetValueResult> getValueResultsWithNoTimestamp; |
| for (auto& result : getGetValueResults()) { |
| ASSERT_GE(result.prop->timestamp, timestamp); |
| GetValueResult resultCopy = result; |
| resultCopy.prop->timestamp = 0; |
| getValueResultsWithNoTimestamp.push_back(std::move(resultCopy)); |
| } |
| ASSERT_THAT(getValueResultsWithNoTimestamp, ContainerEq(expectedGetValueResults)); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testReadValuesErrorInvalidProp) { |
| std::vector<SetValueRequest> setValueRequests; |
| std::vector<SetValueResult> expectedSetValueResults; |
| |
| int64_t requestId = 1; |
| for (auto& value : getTestPropValues()) { |
| addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, value, |
| StatusCode::OK); |
| } |
| |
| // In our implementation, this would finish immediately. |
| StatusCode status = setValues(setValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| std::vector<GetValueRequest> getValueRequests; |
| std::vector<GetValueResult> expectedGetValueResults; |
| VehiclePropValue invalidProp = { |
| .prop = INVALID_PROP_ID, |
| }; |
| addGetValueRequest(getValueRequests, expectedGetValueResults, requestId++, invalidProp, |
| StatusCode::INVALID_ARG); |
| |
| // In our implementation, this would finish immediately. |
| status = getValues(getValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| ASSERT_THAT(getGetValueResults(), ContainerEq(expectedGetValueResults)); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testReadValuesErrorNotAvailable) { |
| std::vector<GetValueRequest> getValueRequests; |
| std::vector<GetValueResult> expectedGetValueResults; |
| // VEHICLE_MAP_SERVICE does not have initial value, 'get' must always return |
| // StatusCode::NOT_AVAILABLE. |
| addGetValueRequest(getValueRequests, expectedGetValueResults, 0, |
| VehiclePropValue{ |
| .prop = VEHICLE_MAP_SERVICE, |
| }, |
| StatusCode::NOT_AVAILABLE); |
| |
| // In our implementation, this would finish immediately. |
| StatusCode status = getValues(getValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| ASSERT_THAT(getGetValueResults(), ContainerEq(expectedGetValueResults)); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testSetStatusMustIgnore) { |
| VehiclePropValue testValue = getTestPropValues()[0]; |
| testValue.status = VehiclePropertyStatus::UNAVAILABLE; |
| |
| std::vector<SetValueRequest> setValueRequests; |
| std::vector<SetValueResult> expectedSetValueResults; |
| |
| int64_t requestId = 1; |
| addSetValueRequest(setValueRequests, expectedSetValueResults, requestId++, testValue, |
| StatusCode::OK); |
| |
| // In our implementation, this would finish immediately. |
| StatusCode status = setValues(setValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| ASSERT_THAT(getSetValueResults(), ContainerEq(expectedSetValueResults)); |
| |
| std::vector<GetValueRequest> getValueRequests; |
| getValueRequests.push_back(GetValueRequest{ |
| .requestId = requestId++, |
| .prop = testValue, |
| }); |
| |
| // In our implementation, this would finish immediately. |
| status = getValues(getValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| ASSERT_EQ(getGetValueResults().size(), static_cast<size_t>(1)); |
| ASSERT_EQ(getGetValueResults()[0].status, StatusCode::OK); |
| // The status should be by-default AVAILABLE for new status. |
| ASSERT_EQ(getGetValueResults()[0].prop->status, VehiclePropertyStatus::AVAILABLE); |
| |
| // Try to set the property again. The status should not be overwritten. |
| status = setValues(setValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| status = getValues(getValueRequests); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| ASSERT_EQ(getGetValueResults().size(), static_cast<size_t>(2)); |
| ASSERT_EQ(getGetValueResults()[1].status, StatusCode::OK); |
| ASSERT_EQ(getGetValueResults()[1].prop->status, VehiclePropertyStatus::AVAILABLE); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testVendorOverrideProperties) { |
| std::string overrideDir = android::base::GetExecutableDirectory() + "/override/"; |
| // Set vendor override directory. |
| FakeVehicleHardwareTestHelper helper(getHardware()); |
| helper.overrideProperties(overrideDir.c_str()); |
| |
| // This is the same as the prop in 'gear_selection.json'. |
| int gearProp = toInt(VehicleProperty::GEAR_SELECTION); |
| |
| auto result = getValue(VehiclePropValue{ |
| .prop = gearProp, |
| }); |
| |
| ASSERT_TRUE(result.ok()) << "expect to get the overridden property ok: " << getStatus(result); |
| ASSERT_EQ(static_cast<size_t>(1), result.value().value.int32Values.size()); |
| ASSERT_EQ(8, result.value().value.int32Values[0]); |
| |
| // If we set the value, it should update despite the override. |
| ASSERT_EQ(setValue(VehiclePropValue{ |
| .prop = gearProp, |
| .value = |
| { |
| .int32Values = {5}, |
| }, |
| .timestamp = elapsedRealtimeNano(), |
| }), |
| StatusCode::OK) |
| << "expect to set the overridden property ok"; |
| |
| result = getValue(VehiclePropValue{ |
| .prop = gearProp, |
| }); |
| |
| ASSERT_TRUE(result.ok()) << "expect to get the overridden property after setting value ok"; |
| ASSERT_EQ(static_cast<size_t>(1), result.value().value.int32Values.size()); |
| ASSERT_EQ(5, result.value().value.int32Values[0]); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesMultipleAreas) { |
| std::string overrideDir = android::base::GetExecutableDirectory() + "/override/"; |
| // Set vendor override directory. |
| FakeVehicleHardwareTestHelper helper(getHardware()); |
| helper.overrideProperties(overrideDir.c_str()); |
| |
| // This is the same as the prop in 'hvac_temperature_set.json'. |
| int hvacProp = toInt(VehicleProperty::HVAC_TEMPERATURE_SET); |
| |
| auto result = getValue(VehiclePropValue{ |
| .prop = hvacProp, |
| .areaId = HVAC_LEFT, |
| }); |
| |
| ASSERT_TRUE(result.ok()) << "expect to get the overridden property ok: " << getStatus(result); |
| ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size()); |
| ASSERT_EQ(30.0f, result.value().value.floatValues[0]); |
| |
| // HVAC_RIGHT should not be affected and return the default value. |
| result = getValue(VehiclePropValue{ |
| .prop = hvacProp, |
| .areaId = HVAC_RIGHT, |
| }); |
| |
| ASSERT_TRUE(result.ok()) << "expect to get the default property ok: " << getStatus(result); |
| ASSERT_EQ(static_cast<size_t>(1), result.value().value.floatValues.size()); |
| ASSERT_EQ(20.0f, result.value().value.floatValues[0]); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesDirDoesNotExist) { |
| // Set vendor override directory to a non-existing dir |
| FakeVehicleHardwareTestHelper helper(getHardware()); |
| helper.overrideProperties("123"); |
| auto result = getValue(VehiclePropValue{ |
| .prop = toInt(VehicleProperty::GEAR_SELECTION), |
| }); |
| |
| ASSERT_TRUE(result.ok()) << "expect to get the default property ok: " << getStatus(result); |
| ASSERT_EQ(static_cast<size_t>(1), result.value().value.int32Values.size()); |
| ASSERT_EQ(4, result.value().value.int32Values[0]); |
| } |
| |
| struct SetSpecialValueTestCase { |
| std::string name; |
| std::vector<VehiclePropValue> valuesToSet; |
| std::vector<VehiclePropValue> expectedValuesToGet; |
| }; |
| |
| std::vector<SetSpecialValueTestCase> setSpecialValueTestCases() { |
| return { |
| SetSpecialValueTestCase{ |
| .name = "set_ap_power_state_report_deep_sleep_exit", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::DEEP_SLEEP_EXIT)}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::DEEP_SLEEP_EXIT)}, |
| }, |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int32Values = {toInt(VehicleApPowerStateReq::ON), |
| 0}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "set_ap_power_state_report_hibernation_exit", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::HIBERNATION_EXIT)}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::HIBERNATION_EXIT)}, |
| }, |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int32Values = {toInt(VehicleApPowerStateReq::ON), |
| 0}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "set_ap_power_state_report_shutdown_cancelled", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::SHUTDOWN_CANCELLED)}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::SHUTDOWN_CANCELLED)}, |
| }, |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int32Values = {toInt(VehicleApPowerStateReq::ON), |
| 0}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "set_ap_power_state_report_wait_for_vhal", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::WAIT_FOR_VHAL)}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::WAIT_FOR_VHAL)}, |
| }, |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int32Values = {toInt(VehicleApPowerStateReq::ON), |
| 0}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "set_ap_power_state_report_deep_sleep_entry", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::DEEP_SLEEP_ENTRY)}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::DEEP_SLEEP_ENTRY)}, |
| }, |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int32Values = |
| {toInt(VehicleApPowerStateReq::FINISHED), 0}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "set_ap_power_state_report_hibernation_entry", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::HIBERNATION_ENTRY)}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::HIBERNATION_ENTRY)}, |
| }, |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int32Values = |
| {toInt(VehicleApPowerStateReq::FINISHED), 0}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "set_ap_power_state_report_shutdown_start", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::SHUTDOWN_START)}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT), |
| .value.int32Values = {toInt( |
| VehicleApPowerStateReport::SHUTDOWN_START)}, |
| }, |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int32Values = |
| {toInt(VehicleApPowerStateReq::FINISHED), 0}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "cluster_report_state_to_vendor", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::CLUSTER_REPORT_STATE), |
| .value.int32Values = {1}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = VENDOR_CLUSTER_REPORT_STATE, |
| .value.int32Values = {1}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "cluster_request_display_to_vendor", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::CLUSTER_REQUEST_DISPLAY), |
| .value.int32Values = {1}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = VENDOR_CLUSTER_REQUEST_DISPLAY, |
| .value.int32Values = {1}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "cluster_navigation_state_to_vendor", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = toInt( |
| VehicleProperty::CLUSTER_NAVIGATION_STATE), |
| .value.byteValues = {0x1}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = VENDOR_CLUSTER_NAVIGATION_STATE, |
| .value.byteValues = {0x1}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "vendor_cluster_switch_ui_to_system", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = VENDOR_CLUSTER_SWITCH_UI, |
| .value.int32Values = {1}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::CLUSTER_SWITCH_UI), |
| .value.int32Values = {1}, |
| }, |
| }, |
| }, |
| SetSpecialValueTestCase{ |
| .name = "vendor_cluster_display_state_to_system", |
| .valuesToSet = |
| { |
| VehiclePropValue{ |
| .prop = VENDOR_CLUSTER_DISPLAY_STATE, |
| .value.int32Values = {1, 2}, |
| }, |
| }, |
| .expectedValuesToGet = |
| { |
| VehiclePropValue{ |
| .prop = toInt(VehicleProperty::CLUSTER_DISPLAY_STATE), |
| .value.int32Values = {1, 2}, |
| }, |
| }, |
| }, |
| }; |
| } |
| |
| class FakeVehicleHardwareSpecialValuesTest |
| : public FakeVehicleHardwareTest, |
| public testing::WithParamInterface<SetSpecialValueTestCase> {}; |
| |
| TEST_P(FakeVehicleHardwareSpecialValuesTest, testSetSpecialProperties) { |
| const SetSpecialValueTestCase& tc = GetParam(); |
| |
| for (const auto& value : tc.valuesToSet) { |
| ASSERT_EQ(setValue(value), StatusCode::OK) << "failed to set property " << value.prop; |
| } |
| |
| std::vector<VehiclePropValue> gotValues; |
| |
| for (const auto& value : tc.expectedValuesToGet) { |
| auto result = getValue(VehiclePropValue{.prop = value.prop}); |
| |
| ASSERT_TRUE(result.ok()) << "failed to get property " << value.prop |
| << " status:" << getStatus(result); |
| |
| gotValues.push_back(result.value()); |
| VehiclePropValue valueWithNoTimestamp = result.value(); |
| valueWithNoTimestamp.timestamp = 0; |
| |
| ASSERT_EQ(valueWithNoTimestamp, value); |
| } |
| |
| // Some of the updated properties might be the same as default config, thus not causing |
| // a property change event. So the changed properties should be a subset of all the updated |
| // properties. |
| ASSERT_THAT(getChangedProperties(), WhenSortedBy(mPropValueCmp, IsSubsetOf(gotValues))); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| SpecialValuesTests, FakeVehicleHardwareSpecialValuesTest, |
| testing::ValuesIn(setSpecialValueTestCases()), |
| [](const testing::TestParamInfo<FakeVehicleHardwareSpecialValuesTest::ParamType>& info) { |
| return info.param.name; |
| }); |
| |
| TEST_F(FakeVehicleHardwareTest, testGetObd2FreezeFrame) { |
| int64_t timestamp = elapsedRealtimeNano(); |
| |
| auto result = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO}); |
| |
| ASSERT_TRUE(result.ok()); |
| |
| auto propValue = result.value(); |
| ASSERT_GE(propValue.timestamp, timestamp); |
| ASSERT_EQ(propValue.value.int64Values.size(), static_cast<size_t>(3)) |
| << "expect 3 obd2 freeze frames stored"; |
| |
| for (int64_t timestamp : propValue.value.int64Values) { |
| auto freezeFrameResult = getValue(VehiclePropValue{ |
| .prop = OBD2_FREEZE_FRAME, |
| .value.int64Values = {timestamp}, |
| }); |
| |
| EXPECT_TRUE(result.ok()) << "expect to get freeze frame for timestamp " << timestamp |
| << " ok"; |
| EXPECT_GE(freezeFrameResult.value().timestamp, timestamp); |
| } |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testClearObd2FreezeFrame) { |
| int64_t timestamp = elapsedRealtimeNano(); |
| |
| auto getValueResult = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO}); |
| |
| ASSERT_TRUE(getValueResult.ok()); |
| |
| auto propValue = getValueResult.value(); |
| ASSERT_GE(propValue.timestamp, timestamp); |
| ASSERT_EQ(propValue.value.int64Values.size(), static_cast<size_t>(3)) |
| << "expect 3 obd2 freeze frames stored"; |
| |
| // No int64Values should clear all freeze frames. |
| StatusCode status = setValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_CLEAR}); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| getValueResult = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO}); |
| |
| ASSERT_TRUE(getValueResult.ok()); |
| ASSERT_EQ(getValueResult.value().value.int64Values.size(), static_cast<size_t>(0)) |
| << "expect 0 obd2 freeze frames after cleared"; |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testSetVehicleMapService) { |
| StatusCode status = |
| setValue(VehiclePropValue{.prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE)}); |
| |
| EXPECT_EQ(status, StatusCode::OK); |
| |
| auto getValueResult = |
| getValue(VehiclePropValue{.prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE)}); |
| |
| EXPECT_FALSE(getValueResult.ok()); |
| EXPECT_EQ(getValueResult.error(), StatusCode::NOT_AVAILABLE); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testGetUserPropertySetOnly) { |
| for (VehicleProperty prop : std::vector<VehicleProperty>({ |
| VehicleProperty::INITIAL_USER_INFO, |
| VehicleProperty::SWITCH_USER, |
| VehicleProperty::CREATE_USER, |
| VehicleProperty::REMOVE_USER, |
| })) { |
| auto result = getValue(VehiclePropValue{.prop = toInt(prop)}); |
| |
| EXPECT_FALSE(result.ok()); |
| if (!result.ok()) { |
| EXPECT_EQ(result.error(), StatusCode::INVALID_ARG); |
| } |
| } |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testGetUserIdAssoc) { |
| int32_t userIdAssocProp = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION); |
| |
| auto result = getValue(VehiclePropValue{.prop = userIdAssocProp}); |
| |
| // Default returns NOT_AVAILABLE. |
| ASSERT_FALSE(result.ok()); |
| ASSERT_EQ(result.error(), StatusCode::NOT_AVAILABLE); |
| |
| // This is the same example as used in User HAL Emulation doc. |
| VehiclePropValue valueToSet = { |
| .prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION), |
| .areaId = 1, |
| .value.int32Values = {666, 1, 1, 2}, |
| }; |
| |
| StatusCode status = setValue(valueToSet); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| result = getValue(VehiclePropValue{ |
| .prop = userIdAssocProp, |
| // Request ID |
| .value.int32Values = {1}, |
| }); |
| |
| ASSERT_TRUE(result.ok()); |
| |
| auto& gotValue = result.value(); |
| gotValue.timestamp = 0; |
| |
| // Expect to get the same request ID. |
| valueToSet.value.int32Values[0] = 1; |
| |
| ASSERT_EQ(gotValue, valueToSet); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testSwitchUser) { |
| // This is the same example as used in User HAL Emulation doc. |
| VehiclePropValue valueToSet = { |
| .prop = toInt(VehicleProperty::SWITCH_USER), |
| .areaId = 1, |
| .value.int32Values = {666, 3, 2}, |
| }; |
| |
| StatusCode status = setValue(valueToSet); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Simulate a request from Android side. |
| VehiclePropValue switchUserRequest = { |
| .prop = toInt(VehicleProperty::SWITCH_USER), |
| .areaId = 0, |
| .value.int32Values = {666, 3}, |
| }; |
| // Clear existing events. |
| clearChangedProperties(); |
| |
| status = setValue(switchUserRequest); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Should generate an event for user hal response. |
| auto events = getChangedProperties(); |
| ASSERT_EQ(events.size(), static_cast<size_t>(1)); |
| |
| events[0].timestamp = 0; |
| ASSERT_EQ(events[0], valueToSet); |
| |
| // Try to get switch_user again, should return default value. |
| clearChangedProperties(); |
| status = setValue(switchUserRequest); |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| events = getChangedProperties(); |
| ASSERT_EQ(events.size(), static_cast<size_t>(1)); |
| events[0].timestamp = 0; |
| ASSERT_EQ(events[0], (VehiclePropValue{ |
| .areaId = 0, |
| .prop = toInt(VehicleProperty::SWITCH_USER), |
| .value.int32Values = |
| { |
| // Request ID |
| 666, |
| // VEHICLE_RESPONSE |
| 3, |
| // SUCCESS |
| 1, |
| }, |
| })); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testCreateUser) { |
| // This is the same example as used in User HAL Emulation doc. |
| VehiclePropValue valueToSet = { |
| .prop = toInt(VehicleProperty::CREATE_USER), |
| .areaId = 1, |
| .value.int32Values = {666, 2}, |
| }; |
| |
| StatusCode status = setValue(valueToSet); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Simulate a request from Android side. |
| VehiclePropValue createUserRequest = { |
| .prop = toInt(VehicleProperty::CREATE_USER), |
| .areaId = 0, |
| .value.int32Values = {666}, |
| }; |
| // Clear existing events. |
| clearChangedProperties(); |
| |
| status = setValue(createUserRequest); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Should generate an event for user hal response. |
| auto events = getChangedProperties(); |
| ASSERT_EQ(events.size(), static_cast<size_t>(1)); |
| events[0].timestamp = 0; |
| EXPECT_EQ(events[0], valueToSet); |
| |
| // Try to get create_user again, should return default value. |
| clearChangedProperties(); |
| status = setValue(createUserRequest); |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| events = getChangedProperties(); |
| ASSERT_EQ(events.size(), static_cast<size_t>(1)); |
| events[0].timestamp = 0; |
| ASSERT_EQ(events[0], (VehiclePropValue{ |
| .areaId = 0, |
| .prop = toInt(VehicleProperty::CREATE_USER), |
| .value.int32Values = |
| { |
| // Request ID |
| 666, |
| // SUCCESS |
| 1, |
| }, |
| })); |
| } |
| |
| TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) { |
| // This is the same example as used in User HAL Emulation doc. |
| VehiclePropValue valueToSet = { |
| .prop = toInt(VehicleProperty::INITIAL_USER_INFO), |
| .areaId = 1, |
| .value.int32Values = {666, 1, 11}, |
| }; |
| |
| StatusCode status = setValue(valueToSet); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Simulate a request from Android side. |
| VehiclePropValue initialUserInfoRequest = { |
| .prop = toInt(VehicleProperty::INITIAL_USER_INFO), |
| .areaId = 0, |
| .value.int32Values = {3}, |
| }; |
| // Clear existing events. |
| clearChangedProperties(); |
| |
| status = setValue(initialUserInfoRequest); |
| |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| // Should generate an event for user hal response. |
| auto events = getChangedProperties(); |
| ASSERT_EQ(events.size(), static_cast<size_t>(1)); |
| events[0].timestamp = 0; |
| EXPECT_EQ(events[0], (VehiclePropValue{ |
| .areaId = 1, |
| .prop = toInt(VehicleProperty::INITIAL_USER_INFO), |
| .value.int32Values = {3, 1, 11}, |
| })); |
| |
| // Try to get create_user again, should return default value. |
| clearChangedProperties(); |
| status = setValue(initialUserInfoRequest); |
| ASSERT_EQ(status, StatusCode::OK); |
| |
| events = getChangedProperties(); |
| ASSERT_EQ(events.size(), static_cast<size_t>(1)); |
| events[0].timestamp = 0; |
| EXPECT_EQ(events[0], (VehiclePropValue{ |
| .areaId = 0, |
| .prop = toInt(VehicleProperty::INITIAL_USER_INFO), |
| .value.int32Values = |
| { |
| // Request ID |
| 3, |
| // ACTION: DEFAULT |
| 0, |
| // User id: 0 |
| 0, |
| // Flags: 0 |
| 0, |
| }, |
| .value.stringValue = "||", |
| })); |
| } |
| |
| } // namespace fake |
| } // namespace vehicle |
| } // namespace automotive |
| } // namespace hardware |
| } // namespace android |