blob: 65585d938e5b79ed0a40b047ceefcfd43feaabcf [file] [log] [blame]
/*
* 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 <FakeObd2Frame.h>
#include <FakeUserHal.h>
#include <PropertyUtils.h>
#include <TestPropertyUtils.h>
#include <android-base/expected.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <inttypes.h>
#include <chrono>
#include <condition_variable>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace aidl {
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
void PrintTo(const VehiclePropValue& value, std::ostream* os) {
*os << "\n( " << value.toString() << " )\n";
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android
} // namespace aidl
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::VehicleHwKeyInputAction;
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 ::aidl::android::hardware::automotive::vehicle::VehicleUnit;
using ::android::base::expected;
using ::android::base::ScopedLockAssertion;
using ::android::base::StringPrintf;
using ::android::base::unexpected;
using ::testing::AnyOfArray;
using ::testing::ContainerEq;
using ::testing::ContainsRegex;
using ::testing::Eq;
using ::testing::HasSubstr;
using ::testing::WhenSortedBy;
using std::chrono::milliseconds;
constexpr int INVALID_PROP_ID = 0;
constexpr char CAR_MAKE[] = "Default Car";
} // namespace
// A helper class to access private methods for FakeVehicleHardware.
class FakeVehicleHardwareTestHelper {
public:
FakeVehicleHardwareTestHelper(FakeVehicleHardware* hardware) { mHardware = hardware; }
std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations() {
return mHardware->loadConfigDeclarations();
}
private:
FakeVehicleHardware* mHardware;
};
class FakeVehicleHardwareTest : public ::testing::Test {
protected:
void SetUp() override {
mHardware = std::make_unique<FakeVehicleHardware>(android::base::GetExecutableDirectory(),
/*overrideConfigDir=*/"",
/*forceOverride=*/false);
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); });
}
void TearDown() override {
// mHardware uses callback which contains reference to 'this', so it has to be destroyed
// before 'this'.
mHardware.reset();
}
FakeVehicleHardware* getHardware() { return mHardware.get(); }
void setHardware(std::unique_ptr<FakeVehicleHardware> hardware) {
mHardware = std::move(hardware);
}
StatusCode setValues(const std::vector<SetValueRequest>& requests) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
for (const auto& request : requests) {
mPendingSetValueRequests.insert(request.requestId);
}
}
if (StatusCode status = getHardware()->setValues(mSetValuesCallback, requests);
status != StatusCode::OK) {
return status;
}
std::unique_lock<std::mutex> lk(mLock);
// Wait for the onSetValueResults.
bool result = mCv.wait_for(lk, milliseconds(1000), [this] {
ScopedLockAssertion lockAssertion(mLock);
return mPendingSetValueRequests.size() == 0;
});
if (!result) {
ALOGE("wait for callbacks for setValues timed-out");
return StatusCode::INTERNAL_ERROR;
}
return StatusCode::OK;
}
StatusCode getValues(const std::vector<GetValueRequest>& requests) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
for (const auto& request : requests) {
mPendingGetValueRequests.insert(request.requestId);
}
}
if (StatusCode status = getHardware()->getValues(mGetValuesCallback, requests);
status != StatusCode::OK) {
return status;
}
std::unique_lock<std::mutex> lk(mLock);
// Wait for the onGetValueResults.
bool result = mCv.wait_for(lk, milliseconds(1000), [this] {
ScopedLockAssertion lockAssertion(mLock);
return mPendingGetValueRequests.size() == 0;
});
if (!result) {
ALOGE("wait for callbacks for getValues timed-out");
return StatusCode::INTERNAL_ERROR;
}
return StatusCode::OK;
}
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) {
std::scoped_lock<std::mutex> lockGuard(mLock);
for (auto& result : results) {
mSetValueResults.push_back(result);
mPendingSetValueRequests.erase(result.requestId);
}
mCv.notify_all();
}
const std::vector<SetValueResult>& getSetValueResults() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mSetValueResults;
}
void onGetValues(std::vector<GetValueResult> results) {
std::scoped_lock<std::mutex> lockGuard(mLock);
for (auto& result : results) {
mGetValueResults.push_back(result);
mPendingGetValueRequests.erase(result.requestId);
}
mCv.notify_all();
}
const std::vector<GetValueResult>& getGetValueResults() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mGetValueResults;
}
void onPropertyChangeEvent(std::vector<VehiclePropValue> values) {
std::scoped_lock<std::mutex> lockGuard(mLock);
for (auto& value : values) {
mChangedProperties.push_back(value);
PropIdAreaId propIdAreaId{
.propId = value.prop,
.areaId = value.areaId,
};
mEventCount[propIdAreaId]++;
}
mCv.notify_all();
}
const std::vector<VehiclePropValue>& getChangedProperties() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mChangedProperties;
}
bool waitForChangedProperties(size_t count, milliseconds timeout) {
std::unique_lock<std::mutex> lk(mLock);
return mCv.wait_for(lk, timeout, [this, count] {
ScopedLockAssertion lockAssertion(mLock);
return mChangedProperties.size() >= count;
});
}
bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count,
milliseconds timeout) {
PropIdAreaId propIdAreaId{
.propId = propId,
.areaId = areaId,
};
std::unique_lock<std::mutex> lk(mLock);
return mCv.wait_for(lk, timeout, [this, propIdAreaId, count] {
ScopedLockAssertion lockAssertion(mLock);
return mEventCount[propIdAreaId] >= count;
});
}
void clearChangedProperties() {
std::scoped_lock<std::mutex> lockGuard(mLock);
mEventCount.clear();
mChangedProperties.clear();
}
size_t getEventCount(int32_t propId, int32_t areaId) {
PropIdAreaId propIdAreaId{
.propId = propId,
.areaId = areaId,
};
std::scoped_lock<std::mutex> lockGuard(mLock);
return mEventCount[propIdAreaId];
}
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:
std::unique_ptr<FakeVehicleHardware> mHardware;
std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback;
std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback;
std::condition_variable mCv;
std::mutex mLock;
std::unordered_map<PropIdAreaId, size_t, PropIdAreaIdHash> mEventCount GUARDED_BY(mLock);
std::vector<SetValueResult> mSetValueResults GUARDED_BY(mLock);
std::vector<GetValueResult> mGetValueResults GUARDED_BY(mLock);
std::vector<VehiclePropValue> mChangedProperties GUARDED_BY(mLock);
std::unordered_set<int64_t> mPendingSetValueRequests GUARDED_BY(mLock);
std::unordered_set<int64_t> mPendingGetValueRequests GUARDED_BY(mLock);
};
TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) {
std::vector<VehiclePropConfig> configs = getHardware()->getAllPropertyConfigs();
FakeVehicleHardwareTestHelper helper(getHardware());
ASSERT_EQ(configs.size(), helper.loadConfigDeclarations().size());
}
TEST_F(FakeVehicleHardwareTest, testGetDefaultValues) {
std::vector<GetValueRequest> getValueRequests;
std::vector<GetValueResult> expectedGetValueResults;
int64_t requestId = 1;
FakeVehicleHardwareTestHelper helper(getHardware());
for (auto& [propId, config] : helper.loadConfigDeclarations()) {
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;
}
if (propId == ECHO_REVERSE_BYTES) {
// Ignore ECHO_REVERSE_BYTES, it has special logic.
continue;
}
if (propId == VENDOR_PROPERTY_ID) {
// Ignore VENDOR_PROPERTY_ID, it has special logic.
continue;
}
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 currentDir = android::base::GetExecutableDirectory();
std::string overrideDir = currentDir + "/override/";
// Set vendor override directory.
std::unique_ptr<FakeVehicleHardware> hardware =
std::make_unique<FakeVehicleHardware>(currentDir, overrideDir, /*forceOverride=*/true);
setHardware(std::move(hardware));
// 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 currentDir = android::base::GetExecutableDirectory();
std::string overrideDir = currentDir + "/override/";
// Set vendor override directory.
std::unique_ptr<FakeVehicleHardware> hardware =
std::make_unique<FakeVehicleHardware>(currentDir, overrideDir, /*forceOverride=*/true);
setHardware(std::move(hardware));
// 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]);
}
TEST_F(FakeVehicleHardwareTest, testVendorOverridePropertiesDirDoesNotExist) {
std::string currentDir = android::base::GetExecutableDirectory();
std::string overrideDir = currentDir + "/override/";
// Set vendor override directory to a non-existing dir.
std::unique_ptr<FakeVehicleHardware> hardware =
std::make_unique<FakeVehicleHardware>(currentDir, "1234", /*forceOverride=*/true);
setHardware(std::move(hardware));
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_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values = {toInt(VehicleApPowerStateReq::ON),
0},
},
VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
.value.int32Values = {toInt(
VehicleApPowerStateReport::DEEP_SLEEP_EXIT)},
},
},
},
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_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values = {toInt(VehicleApPowerStateReq::ON),
0},
},
VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
.value.int32Values = {toInt(
VehicleApPowerStateReport::HIBERNATION_EXIT)},
},
},
},
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_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values = {toInt(VehicleApPowerStateReq::ON),
0},
},
VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
.value.int32Values = {toInt(
VehicleApPowerStateReport::SHUTDOWN_CANCELLED)},
},
},
},
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_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values = {toInt(VehicleApPowerStateReq::ON),
0},
},
VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
.value.int32Values = {toInt(
VehicleApPowerStateReport::WAIT_FOR_VHAL)},
},
},
},
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_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values =
{toInt(VehicleApPowerStateReq::FINISHED), 0},
},
VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
.value.int32Values = {toInt(
VehicleApPowerStateReport::DEEP_SLEEP_ENTRY)},
},
},
},
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_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values =
{toInt(VehicleApPowerStateReq::FINISHED), 0},
},
VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
.value.int32Values = {toInt(
VehicleApPowerStateReport::HIBERNATION_ENTRY)},
},
},
},
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_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values =
{toInt(VehicleApPowerStateReq::FINISHED), 0},
},
VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REPORT),
.value.int32Values = {toInt(
VehicleApPowerStateReport::SHUTDOWN_START)},
},
},
},
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, Eq(gotValues)));
}
INSTANTIATE_TEST_SUITE_P(
SpecialValuesTests, FakeVehicleHardwareSpecialValuesTest,
testing::ValuesIn(setSpecialValueTestCases()),
[](const testing::TestParamInfo<FakeVehicleHardwareSpecialValuesTest::ParamType>& info) {
return info.param.name;
});
TEST_F(FakeVehicleHardwareTest, testSetWaitForVhalAfterCarServiceCrash) {
int32_t propId = toInt(VehicleProperty::AP_POWER_STATE_REPORT);
VehiclePropValue request = VehiclePropValue{
.prop = propId,
.value.int32Values = {toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL)},
};
ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId;
// Clear existing events.
clearChangedProperties();
// Simulate a Car Service crash, Car Service would restart and send the message again.
ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId;
std::vector<VehiclePropValue> events = getChangedProperties();
// Even though the state is already ON, we should receive another ON event.
ASSERT_EQ(events.size(), 1u);
// Erase the timestamp for comparison.
events[0].timestamp = 0;
auto expectedValue = VehiclePropValue{
.prop = toInt(VehicleProperty::AP_POWER_STATE_REQ),
.status = VehiclePropertyStatus::AVAILABLE,
.value.int32Values = {toInt(VehicleApPowerStateReq::ON), 0},
};
ASSERT_EQ(events[0], expectedValue);
}
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, testGetHvacPropNotAvailable) {
int seatAreaIds[5] = {SEAT_1_LEFT, SEAT_1_RIGHT, SEAT_2_LEFT, SEAT_2_CENTER, SEAT_2_RIGHT};
for (int areaId : seatAreaIds) {
StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.areaId = areaId,
.value.int32Values = {0}});
ASSERT_EQ(status, StatusCode::OK);
for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
int powerPropId = HVAC_POWER_PROPERTIES[i];
for (int powerDependentAreaId : seatAreaIds) {
auto getValueResult = getValue(VehiclePropValue{
.prop = powerPropId,
.areaId = powerDependentAreaId,
});
if (areaId == powerDependentAreaId) {
EXPECT_FALSE(getValueResult.ok());
EXPECT_EQ(getValueResult.error(), StatusCode::NOT_AVAILABLE);
} else {
EXPECT_TRUE(getValueResult.ok());
}
}
}
// Resetting HVAC_POWER_ON at areaId back to ON state to ensure that there's no dependence
// on this value from any power dependent property values other than those with the same
// areaId.
setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.areaId = areaId,
.value.int32Values = {1}});
}
}
TEST_F(FakeVehicleHardwareTest, testSetHvacPropNotAvailable) {
int seatAreaIds[5] = {SEAT_1_LEFT, SEAT_1_RIGHT, SEAT_2_LEFT, SEAT_2_CENTER, SEAT_2_RIGHT};
for (int areaId : seatAreaIds) {
StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.areaId = areaId,
.value.int32Values = {0}});
ASSERT_EQ(status, StatusCode::OK);
for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
int powerPropId = HVAC_POWER_PROPERTIES[i];
for (int powerDependentAreaId : seatAreaIds) {
StatusCode status = setValue(VehiclePropValue{.prop = powerPropId,
.areaId = powerDependentAreaId,
.value.int32Values = {1}});
if (areaId == powerDependentAreaId) {
EXPECT_EQ(status, StatusCode::NOT_AVAILABLE);
} else {
EXPECT_EQ(status, StatusCode::OK);
}
}
}
// Resetting HVAC_POWER_ON at areaId back to ON state to ensure that there's no dependence
// on this value from any power dependent property values other than those with the same
// areaId.
setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.areaId = areaId,
.value.int32Values = {1}});
}
}
TEST_F(FakeVehicleHardwareTest, testHvacPowerOnSendCurrentHvacPropValues) {
int seatAreaIds[5] = {SEAT_1_LEFT, SEAT_1_RIGHT, SEAT_2_LEFT, SEAT_2_CENTER, SEAT_2_RIGHT};
for (int areaId : seatAreaIds) {
StatusCode status = setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.areaId = areaId,
.value.int32Values = {0}});
ASSERT_EQ(status, StatusCode::OK);
clearChangedProperties();
setValue(VehiclePropValue{.prop = toInt(VehicleProperty::HVAC_POWER_ON),
.areaId = areaId,
.value.int32Values = {1}});
auto events = getChangedProperties();
// If we turn HVAC power on, we expect to receive one property event for every HVAC prop
// areas plus one event for HVAC_POWER_ON.
std::vector<int32_t> changedPropIds;
for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
changedPropIds.push_back(HVAC_POWER_PROPERTIES[i]);
}
changedPropIds.push_back(toInt(VehicleProperty::HVAC_POWER_ON));
for (const auto& event : events) {
EXPECT_EQ(event.areaId, areaId);
EXPECT_THAT(event.prop, AnyOfArray(changedPropIds));
}
}
}
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;
// The returned event will have area ID 0.
valueToSet.areaId = 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;
auto expectedValue = VehiclePropValue{
.areaId = 0,
.prop = toInt(VehicleProperty::SWITCH_USER),
.value.int32Values =
{
// Request ID
666,
// VEHICLE_RESPONSE
3,
// SUCCESS
1,
},
};
ASSERT_EQ(events[0], expectedValue);
}
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;
// The returned event will have area ID 0.
valueToSet.areaId = 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;
auto expectedValue = VehiclePropValue{
.areaId = 0,
.prop = toInt(VehicleProperty::CREATE_USER),
.value.int32Values =
{
// Request ID
666,
// SUCCESS
1,
},
};
ASSERT_EQ(events[0], expectedValue);
}
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;
auto expectedValue = VehiclePropValue{
.areaId = 0,
.prop = toInt(VehicleProperty::INITIAL_USER_INFO),
.value.int32Values = {3, 1, 11},
};
EXPECT_EQ(events[0], expectedValue);
// 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;
expectedValue = 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 = "||",
};
EXPECT_EQ(events[0], expectedValue);
}
TEST_F(FakeVehicleHardwareTest, testDumpAllProperties) {
std::vector<std::string> options;
DumpResult result = getHardware()->dump(options);
ASSERT_TRUE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex("dumping .+ properties"));
}
TEST_F(FakeVehicleHardwareTest, testDumpHelp) {
std::vector<std::string> options;
options.push_back("--help");
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex("Usage: "));
}
TEST_F(FakeVehicleHardwareTest, testDumpListProperties) {
std::vector<std::string> options;
options.push_back("--list");
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex("listing .+ properties"));
}
TEST_F(FakeVehicleHardwareTest, testDumpSpecificProperties) {
std::vector<std::string> options;
options.push_back("--get");
std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
std::string prop2 = std::to_string(toInt(VehicleProperty::TIRE_PRESSURE));
options.push_back(prop1);
options.push_back(prop2);
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer,
ContainsRegex(StringPrintf("1:.*prop: %s.*\n2-0:.*prop: %s.*\n2-1:.*prop: %s.*\n",
prop1.c_str(), prop2.c_str(), prop2.c_str())));
}
TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesInvalidProp) {
std::vector<std::string> options;
options.push_back("--get");
std::string prop1 = std::to_string(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
std::string prop2 = std::to_string(INVALID_PROP_ID);
options.push_back(prop1);
options.push_back(prop2);
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex(StringPrintf("1:.*prop: %s.*\nNo property %d\n",
prop1.c_str(), INVALID_PROP_ID)));
}
TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesNoArg) {
std::vector<std::string> options;
options.push_back("--get");
// No arguments.
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex("Invalid number of arguments"));
}
TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertyWithArg) {
auto getValueResult = getValue(VehiclePropValue{.prop = OBD2_FREEZE_FRAME_INFO});
ASSERT_TRUE(getValueResult.ok());
auto propValue = getValueResult.value();
ASSERT_EQ(propValue.value.int64Values.size(), static_cast<size_t>(3))
<< "expect 3 obd2 freeze frames stored";
std::string propIdStr = StringPrintf("%d", OBD2_FREEZE_FRAME);
DumpResult result;
for (int64_t timestamp : propValue.value.int64Values) {
result = getHardware()->dump(
{"--getWithArg", propIdStr, "-i64", StringPrintf("%" PRId64, timestamp)});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex("Get property result:"));
}
// Set the timestamp argument to 0.
result = getHardware()->dump({"--getWithArg", propIdStr, "-i64", "0"});
ASSERT_FALSE(result.callerShouldDumpState);
// There is no freeze obd2 frame at timestamp 0.
ASSERT_THAT(result.buffer, ContainsRegex("failed to read property value"));
}
TEST_F(FakeVehicleHardwareTest, testSaveRestoreProp) {
int32_t prop = toInt(VehicleProperty::TIRE_PRESSURE);
std::string propIdStr = std::to_string(prop);
std::string areaIdStr = std::to_string(WHEEL_FRONT_LEFT);
DumpResult result = getHardware()->dump({"--save-prop", propIdStr, "-a", areaIdStr});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, ContainsRegex("saved"));
ASSERT_EQ(setValue(VehiclePropValue{
.prop = prop,
.areaId = WHEEL_FRONT_LEFT,
.value =
{
.floatValues = {210.0},
},
}),
StatusCode::OK);
result = getHardware()->dump({"--restore-prop", propIdStr, "-a", areaIdStr});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, ContainsRegex("restored"));
auto getResult = getValue(VehiclePropValue{.prop = prop, .areaId = WHEEL_FRONT_LEFT});
ASSERT_TRUE(getResult.ok());
// The default value is 200.0.
ASSERT_EQ(getResult.value().value.floatValues, std::vector<float>{200.0});
}
TEST_F(FakeVehicleHardwareTest, testDumpInjectEvent) {
int32_t prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
std::string propIdStr = std::to_string(prop);
int64_t timestamp = elapsedRealtimeNano();
// Inject an event with float value 123.4 and timestamp.
DumpResult result = getHardware()->dump(
{"--inject-event", propIdStr, "-f", "123.4", "-t", std::to_string(timestamp)});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer,
ContainsRegex(StringPrintf("Event for property: %d injected", prop)));
ASSERT_TRUE(waitForChangedProperties(prop, 0, /*count=*/1, milliseconds(1000)))
<< "No changed event received for injected event from vehicle bus";
auto events = getChangedProperties();
ASSERT_EQ(events.size(), 1u);
auto event = events[0];
ASSERT_EQ(event.timestamp, timestamp);
ASSERT_EQ(event.value.floatValues, std::vector<float>({123.4}));
}
TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
std::vector<std::string> options;
options.push_back("--invalid");
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
}
TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHal) {
std::vector<std::string> options;
options.push_back("--user-hal");
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer,
ContainsRegex("No InitialUserInfo response\nNo SwitchUser response\nNo CreateUser "
"response\nNo SetUserIdentificationAssociation response\n"));
}
struct SetPropTestCase {
std::string test_name;
std::vector<std::string> options;
bool success;
std::string errorMsg = "";
};
class FakeVehicleHardwareSetPropTest : public FakeVehicleHardwareTest,
public testing::WithParamInterface<SetPropTestCase> {};
TEST_P(FakeVehicleHardwareSetPropTest, cmdSetOneProperty) {
const SetPropTestCase& tc = GetParam();
DumpResult result = getHardware()->dump(tc.options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
if (tc.success) {
ASSERT_THAT(result.buffer, ContainsRegex("Set property:"));
} else {
ASSERT_THAT(result.buffer, ContainsRegex(tc.errorMsg));
}
}
std::vector<SetPropTestCase> GenSetPropParams() {
std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE));
return {
{"success_set_string", {"--set", infoMakeProperty, "-s", CAR_MAKE}, true},
{"success_set_bytes", {"--set", infoMakeProperty, "-b", "0xdeadbeef"}, true},
{"success_set_bytes_caps", {"--set", infoMakeProperty, "-b", "0xDEADBEEF"}, true},
{"success_set_int", {"--set", infoMakeProperty, "-i", "2147483647"}, true},
{"success_set_ints",
{"--set", infoMakeProperty, "-i", "2147483647", "0", "-2147483648"},
true},
{"success_set_int64",
{"--set", infoMakeProperty, "-i64", "-9223372036854775808"},
true},
{"success_set_int64s",
{"--set", infoMakeProperty, "-i64", "-9223372036854775808", "0",
"9223372036854775807"},
true},
{"success_set_float", {"--set", infoMakeProperty, "-f", "1.175494351E-38"}, true},
{"success_set_floats",
{"--set", infoMakeProperty, "-f", "-3.402823466E+38", "0", "3.402823466E+38"},
true},
{"success_set_area", {"--set", infoMakeProperty, "-a", "2147483647"}, true},
{"fail_no_options", {"--set", infoMakeProperty}, false, "Invalid number of arguments"},
{"fail_less_than_4_options",
{"--set", infoMakeProperty, "-i"},
false,
"No values specified"},
{"fail_unknown_options", {"--set", infoMakeProperty, "-abcd"}, false, "Unknown option"},
{"fail_invalid_property",
{"--set", "not valid", "-s", CAR_MAKE},
false,
"not a valid int"},
{"fail_duplicate_string",
{"--set", infoMakeProperty, "-s", CAR_MAKE, "-s", CAR_MAKE},
false,
"Duplicate \"-s\" options"},
{"fail_multiple_strings",
{"--set", infoMakeProperty, "-s", CAR_MAKE, CAR_MAKE},
false,
"Expect exact one value"},
{"fail_no_string_value",
{"--set", infoMakeProperty, "-s", "-a", "1234"},
false,
"Expect exact one value"},
{"fail_duplicate_bytes",
{"--set", infoMakeProperty, "-b", "0xdeadbeef", "-b", "0xdeadbeef"},
false,
"Duplicate \"-b\" options"},
{"fail_multiple_bytes",
{"--set", infoMakeProperty, "-b", "0xdeadbeef", "0xdeadbeef"},
false,
"Expect exact one value"},
{"fail_invalid_bytes",
{"--set", infoMakeProperty, "-b", "0xgood"},
false,
"not a valid hex string"},
{"fail_invalid_bytes_no_prefix",
{"--set", infoMakeProperty, "-b", "deadbeef"},
false,
"not a valid hex string"},
{"fail_invalid_int",
{"--set", infoMakeProperty, "-i", "abc"},
false,
"not a valid int"},
{"fail_int_out_of_range",
{"--set", infoMakeProperty, "-i", "2147483648"},
false,
"not a valid int"},
{"fail_no_int_value",
{"--set", infoMakeProperty, "-i", "-s", CAR_MAKE},
false,
"No values specified"},
{"fail_invalid_int64",
{"--set", infoMakeProperty, "-i64", "abc"},
false,
"not a valid int64"},
{"fail_int64_out_of_range",
{"--set", infoMakeProperty, "-i64", "-9223372036854775809"},
false,
"not a valid int64"},
{"fail_no_int64_value",
{"--set", infoMakeProperty, "-i64", "-s", CAR_MAKE},
false,
"No values specified"},
{"fail_invalid_float",
{"--set", infoMakeProperty, "-f", "abc"},
false,
"not a valid float"},
{"fail_float_out_of_range",
{"--set", infoMakeProperty, "-f", "-3.402823466E+39"},
false,
"not a valid float"},
{"fail_no_float_value",
{"--set", infoMakeProperty, "-f", "-s", CAR_MAKE},
false,
"No values specified"},
{"fail_multiple_areas",
{"--set", infoMakeProperty, "-a", "2147483648", "0"},
false,
"Expect exact one value"},
{"fail_invalid_area",
{"--set", infoMakeProperty, "-a", "abc"},
false,
"not a valid int"},
{"fail_area_out_of_range",
{"--set", infoMakeProperty, "-a", "2147483648"},
false,
"not a valid int"},
{"fail_no_area_value",
{"--set", infoMakeProperty, "-a", "-s", CAR_MAKE},
false,
"Expect exact one value"},
};
}
INSTANTIATE_TEST_SUITE_P(
FakeVehicleHardwareSetPropTests, FakeVehicleHardwareSetPropTest,
testing::ValuesIn(GenSetPropParams()),
[](const testing::TestParamInfo<FakeVehicleHardwareSetPropTest::ParamType>& info) {
return info.param.test_name;
});
TEST_F(FakeVehicleHardwareTest, SetComplexPropTest) {
std::string infoMakeProperty = std::to_string(toInt(VehicleProperty::INFO_MAKE));
getHardware()->dump({"--set", infoMakeProperty, "-s", CAR_MAKE,
"-b", "0xdeadbeef", "-i", "2147483647",
"0", "-2147483648", "-i64", "-9223372036854775808",
"0", "9223372036854775807", "-f", "-3.402823466E+38",
"0", "3.402823466E+38", "-a", "123"});
VehiclePropValue requestProp;
requestProp.prop = toInt(VehicleProperty::INFO_MAKE);
requestProp.areaId = 123;
auto result = getValue(requestProp);
ASSERT_TRUE(result.ok());
VehiclePropValue value = result.value();
ASSERT_EQ(value.prop, toInt(VehicleProperty::INFO_MAKE));
ASSERT_EQ(value.areaId, 123);
ASSERT_STREQ(CAR_MAKE, value.value.stringValue.c_str());
uint8_t bytes[] = {0xde, 0xad, 0xbe, 0xef};
ASSERT_FALSE(memcmp(bytes, value.value.byteValues.data(), sizeof(bytes)));
ASSERT_EQ(3u, value.value.int32Values.size());
ASSERT_EQ(2147483647, value.value.int32Values[0]);
ASSERT_EQ(0, value.value.int32Values[1]);
ASSERT_EQ(-2147483648, value.value.int32Values[2]);
ASSERT_EQ(3u, value.value.int64Values.size());
// -9223372036854775808 is not a valid literal since '-' and '9223372036854775808' would be two
// tokens and the later does not fit in unsigned long long.
ASSERT_EQ(-9223372036854775807 - 1, value.value.int64Values[0]);
ASSERT_EQ(0, value.value.int64Values[1]);
ASSERT_EQ(9223372036854775807, value.value.int64Values[2]);
ASSERT_EQ(3u, value.value.floatValues.size());
ASSERT_EQ(-3.402823466E+38f, value.value.floatValues[0]);
ASSERT_EQ(0.0f, value.value.floatValues[1]);
ASSERT_EQ(3.402823466E+38f, value.value.floatValues[2]);
}
struct OptionsTestCase {
std::string name;
std::vector<std::string> options;
std::string expectMsg;
};
class FakeVehicleHardwareOptionsTest : public FakeVehicleHardwareTest,
public testing::WithParamInterface<OptionsTestCase> {};
std::vector<OptionsTestCase> GenInvalidOptions() {
return {{"unknown_command", {"--unknown"}, "Invalid option: --unknown"},
{"help", {"--help"}, "Usage:"},
{"genfakedata_no_subcommand",
{"--genfakedata"},
"No subcommand specified for genfakedata"},
{"genfakedata_unknown_subcommand",
{"--genfakedata", "--unknown"},
"Unknown command: \"--unknown\""},
{"genfakedata_start_linear_no_args",
{"--genfakedata", "--startlinear"},
"incorrect argument count"},
{"genfakedata_start_linear_invalid_propId",
{"--genfakedata", "--startlinear", "abcd", "0.1", "0.1", "0.1", "0.1", "100000000"},
"failed to parse propId as int: \"abcd\""},
{"genfakedata_start_linear_invalid_middleValue",
{"--genfakedata", "--startlinear", "1", "abcd", "0.1", "0.1", "0.1", "100000000"},
"failed to parse middleValue as float: \"abcd\""},
{"genfakedata_start_linear_invalid_currentValue",
{"--genfakedata", "--startlinear", "1", "0.1", "abcd", "0.1", "0.1", "100000000"},
"failed to parse currentValue as float: \"abcd\""},
{"genfakedata_start_linear_invalid_dispersion",
{"--genfakedata", "--startlinear", "1", "0.1", "0.1", "abcd", "0.1", "100000000"},
"failed to parse dispersion as float: \"abcd\""},
{"genfakedata_start_linear_invalid_increment",
{"--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "abcd", "100000000"},
"failed to parse increment as float: \"abcd\""},
{"genfakedata_start_linear_invalid_interval",
{"--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "0.1", "0.1"},
"failed to parse interval as int: \"0.1\""},
{"genfakedata_stop_linear_no_args",
{"--genfakedata", "--stoplinear"},
"incorrect argument count"},
{"genfakedata_stop_linear_invalid_propId",
{"--genfakedata", "--stoplinear", "abcd"},
"failed to parse propId as int: \"abcd\""},
{"genfakedata_startjson_no_args",
{"--genfakedata", "--startjson"},
"incorrect argument count"},
{"genfakedata_startjson_invalid_repetition",
{"--genfakedata", "--startjson", "--path", "file", "0.1"},
"failed to parse repetition as int: \"0.1\""},
{"genfakedata_startjson_invalid_json_file",
{"--genfakedata", "--startjson", "--path", "file", "1"},
"invalid JSON file"},
{"genfakedata_stopjson_no_args",
{"--genfakedata", "--stopjson"},
"incorrect argument count"},
{"genfakedata_keypress_no_args",
{"--genfakedata", "--keypress"},
"incorrect argument count"},
{"genfakedata_keypress_invalid_keyCode",
{"--genfakedata", "--keypress", "0.1", "1"},
"failed to parse keyCode as int: \"0.1\""},
{"genfakedata_keypress_invalid_display",
{"--genfakedata", "--keypress", "1", "0.1"},
"failed to parse display as int: \"0.1\""},
{"genfakedata_keyinputv2_incorrect_arguments",
{"--genfakedata", "--keyinputv2", "1", "1"},
"incorrect argument count, need 7 arguments for --genfakedata --keyinputv2\n"},
{"genfakedata_keyinputv2_invalid_area",
{"--genfakedata", "--keyinputv2", "0.1", "1", "1", "1", "1"},
"failed to parse area as int: \"0.1\""},
{"genfakedata_keyinputv2_invalid_display",
{"--genfakedata", "--keyinputv2", "1", "0.1", "1", "1", "1"},
"failed to parse display as int: \"0.1\""},
{"genfakedata_keyinputv2_invalid_keycode",
{"--genfakedata", "--keyinputv2", "1", "1", "0.1", "1", "1"},
"failed to parse keyCode as int: \"0.1\""},
{"genfakedata_keyinputv2_invalid_action",
{"--genfakedata", "--keyinputv2", "1", "1", "1", "0.1", "1"},
"failed to parse action as int: \"0.1\""},
{"genfakedata_keyinputv2_invalid_repeatcount",
{"--genfakedata", "--keyinputv2", "1", "1", "1", "1", "0.1"},
"failed to parse repeatCount as int: \"0.1\""},
{"genfakedata_motioninput_invalid_argument_count",
{"--genfakedata", "--motioninput", "1", "1", "1", "1", "1"},
"incorrect argument count, need at least 14 arguments for --genfakedata "
"--motioninput including at least 1 --pointer\n"},
{"genfakedata_motioninput_pointer_invalid_argument_count",
{"--genfakedata", "--motioninput", "1", "1", "1", "1", "1", "--pointer", "1", "1", "1",
"1", "1", "1", "--pointer"},
"incorrect argument count, need 6 arguments for every --pointer\n"},
{"genfakedata_motioninput_invalid_area",
{"--genfakedata", "--motioninput", "0.1", "1", "1", "1", "1", "--pointer", "1", "1",
"1", "1", "1", "1"},
"failed to parse area as int: \"0.1\""},
{"genfakedata_motioninput_invalid_display",
{"--genfakedata", "--motioninput", "1", "0.1", "1", "1", "1", "--pointer", "1", "1",
"1", "1", "1", "1"},
"failed to parse display as int: \"0.1\""},
{"genfakedata_motioninput_invalid_inputtype",
{"--genfakedata", "--motioninput", "1", "1", "0.1", "1", "1", "--pointer", "1", "1",
"1", "1", "1", "1"},
"failed to parse inputType as int: \"0.1\""},
{"genfakedata_motioninput_invalid_action",
{"--genfakedata", "--motioninput", "1", "1", "1", "0.1", "1", "--pointer", "1", "1",
"1", "1", "1", "1"},
"failed to parse action as int: \"0.1\""},
{"genfakedata_motioninput_invalid_buttonstate",
{"--genfakedata", "--motioninput", "1", "1", "1", "1", "0.1", "--pointer", "1", "1",
"1.2", "1.2", "1.2", "1.2"},
"failed to parse buttonState as int: \"0.1\""},
{"genfakedata_motioninput_invalid_pointerid",
{"--genfakedata", "--motioninput", "1", "1", "1", "1", "1", "--pointer", "0.1", "1",
"1.2", "1", "1", "1"},
"failed to parse pointerId as int: \"0.1\""},
{"genfakedata_motioninput_invalid_tooltype",
{"--genfakedata", "--motioninput", "1", "1", "1", "1", "1", "--pointer", "1", "0.1",
"1.2", "1", "1", "1"},
"failed to parse toolType as int: \"0.1\""}};
}
TEST_P(FakeVehicleHardwareOptionsTest, testInvalidOptions) {
auto tc = GetParam();
DumpResult result = getHardware()->dump(tc.options);
EXPECT_FALSE(result.callerShouldDumpState);
EXPECT_THAT(result.buffer, HasSubstr(tc.expectMsg));
}
INSTANTIATE_TEST_SUITE_P(
FakeVehicleHardwareOptionsTests, FakeVehicleHardwareOptionsTest,
testing::ValuesIn(GenInvalidOptions()),
[](const testing::TestParamInfo<FakeVehicleHardwareOptionsTest::ParamType>& info) {
return info.param.name;
});
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) {
// Start a fake linear data generator for vehicle speed at 0.1s interval.
// range: 0 - 100, current value: 30, step: 20.
std::string propIdString = StringPrintf("%d", toInt(VehicleProperty::PERF_VEHICLE_SPEED));
std::vector<std::string> options = {"--genfakedata", "--startlinear", propIdString,
/*middleValue=*/"50",
/*currentValue=*/"30",
/*dispersion=*/"50",
/*increment=*/"20",
/*interval=*/"100000000"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
ASSERT_TRUE(waitForChangedProperties(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0, /*count=*/5,
milliseconds(1000)))
<< "not enough events generated for linear data generator";
int32_t value = 30;
auto events = getChangedProperties();
for (size_t i = 0; i < 5; i++) {
ASSERT_EQ(1u, events[i].value.floatValues.size());
EXPECT_EQ(static_cast<float>(value), events[i].value.floatValues[0]);
value = (value + 20) % 100;
}
// Stop the linear generator.
options = {"--genfakedata", "--stoplinear", propIdString};
result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
clearChangedProperties();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// There should be no new events generated.
EXPECT_EQ(0u, getEventCount(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0));
}
std::string getTestFilePath(const char* filename) {
static std::string baseDir = android::base::GetExecutableDirectory();
return baseDir + "/fakedata/" + filename;
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) {
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("prop.json"), "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
ASSERT_TRUE(waitForChangedProperties(/*count=*/8, milliseconds(1000)))
<< "not enough events generated for JSON data generator";
auto events = getChangedProperties();
ASSERT_EQ(8u, events.size());
// First set of events, we test 1st and the last.
EXPECT_EQ(1u, events[0].value.int32Values.size());
EXPECT_EQ(8, events[0].value.int32Values[0]);
EXPECT_EQ(1u, events[3].value.int32Values.size());
EXPECT_EQ(10, events[3].value.int32Values[0]);
// Second set of the same events.
EXPECT_EQ(1u, events[4].value.int32Values.size());
EXPECT_EQ(8, events[4].value.int32Values[0]);
EXPECT_EQ(1u, events[7].value.int32Values.size());
EXPECT_EQ(10, events[7].value.int32Values[0]);
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonByContent) {
std::vector<std::string> options = {
"--genfakedata", "--startjson", "--content",
"[{\"timestamp\":1000000,\"areaId\":0,\"value\":8,\"prop\":289408000}]", "1"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
ASSERT_TRUE(waitForChangedProperties(/*count=*/1, milliseconds(1000)))
<< "not enough events generated for JSON data generator";
auto events = getChangedProperties();
ASSERT_EQ(1u, events.size());
EXPECT_EQ(1u, events[0].value.int32Values.size());
EXPECT_EQ(8, events[0].value.int32Values[0]);
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonInvalidContent) {
std::vector<std::string> options = {"--genfakedata", "--startjson", "--content", "[{", "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("invalid JSON content"));
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonInvalidFile) {
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("blahblah.json"), "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("invalid JSON file"));
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStop) {
// No iteration number provided, would loop indefinitely.
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("prop.json")};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
std::string id = result.buffer.substr(result.buffer.find("ID: ") + 4);
result = getHardware()->dump({"--genfakedata", "--stopjson", id});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStopInvalidFile) {
// No iteration number provided, would loop indefinitely.
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("prop.json")};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
result = getHardware()->dump({"--genfakedata", "--stopjson", "1234"});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("No JSON event generator found"));
// TearDown function should destroy the generator which stops the iteration.
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) {
std::vector<std::string> options = {"--genfakedata", "--keypress", "1", "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
auto events = getChangedProperties();
ASSERT_EQ(2u, events.size());
EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[0].prop);
EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[1].prop);
ASSERT_EQ(3u, events[0].value.int32Values.size());
ASSERT_EQ(3u, events[1].value.int32Values.size());
EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_DOWN), events[0].value.int32Values[0]);
EXPECT_EQ(1, events[0].value.int32Values[1]);
EXPECT_EQ(2, events[0].value.int32Values[2]);
EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_UP), events[1].value.int32Values[0]);
EXPECT_EQ(1, events[1].value.int32Values[1]);
EXPECT_EQ(2, events[1].value.int32Values[2]);
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyInputV2) {
std::vector<std::string> options = {"--genfakedata", "--keyinputv2", "1", "2", "3", "4", "5"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
auto events = getChangedProperties();
ASSERT_EQ(1u, events.size());
EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT_V2), events[0].prop);
ASSERT_EQ(4u, events[0].value.int32Values.size());
EXPECT_EQ(2, events[0].value.int32Values[0]);
EXPECT_EQ(3, events[0].value.int32Values[1]);
EXPECT_EQ(4, events[0].value.int32Values[2]);
EXPECT_EQ(5, events[0].value.int32Values[3]);
ASSERT_EQ(1u, events[0].value.int64Values.size());
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataMotionInput) {
std::vector<std::string> options = {"--genfakedata",
"--motioninput",
"1",
"2",
"3",
"4",
"5",
"--pointer",
"11",
"22",
"33.3",
"44.4",
"55.5",
"66.6",
"--pointer",
"21",
"32",
"43.3",
"54.4",
"65.5",
"76.6"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
auto events = getChangedProperties();
ASSERT_EQ(1u, events.size());
EXPECT_EQ(toInt(VehicleProperty::HW_MOTION_INPUT), events[0].prop);
ASSERT_EQ(9u, events[0].value.int32Values.size());
EXPECT_EQ(2, events[0].value.int32Values[0]);
EXPECT_EQ(3, events[0].value.int32Values[1]);
EXPECT_EQ(4, events[0].value.int32Values[2]);
EXPECT_EQ(5, events[0].value.int32Values[3]);
EXPECT_EQ(2, events[0].value.int32Values[4]);
EXPECT_EQ(11, events[0].value.int32Values[5]);
EXPECT_EQ(21, events[0].value.int32Values[6]);
EXPECT_EQ(22, events[0].value.int32Values[7]);
EXPECT_EQ(32, events[0].value.int32Values[8]);
ASSERT_EQ(8u, events[0].value.floatValues.size());
EXPECT_FLOAT_EQ(33.3, events[0].value.floatValues[0]);
EXPECT_FLOAT_EQ(43.3, events[0].value.floatValues[1]);
EXPECT_FLOAT_EQ(44.4, events[0].value.floatValues[2]);
EXPECT_FLOAT_EQ(54.4, events[0].value.floatValues[3]);
EXPECT_FLOAT_EQ(55.5, events[0].value.floatValues[4]);
EXPECT_FLOAT_EQ(65.5, events[0].value.floatValues[5]);
EXPECT_FLOAT_EQ(66.6, events[0].value.floatValues[6]);
EXPECT_FLOAT_EQ(76.6, events[0].value.floatValues[7]);
ASSERT_EQ(1u, events[0].value.int64Values.size());
}
TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) {
ASSERT_EQ(setValue(VehiclePropValue{
.prop = ECHO_REVERSE_BYTES,
.value =
{
.byteValues = {0x01, 0x02, 0x03, 0x04},
},
}),
StatusCode::OK);
auto result = getValue(VehiclePropValue{
.prop = ECHO_REVERSE_BYTES,
});
ASSERT_TRUE(result.ok()) << "failed to get ECHO_REVERSE_BYTES value: " << getStatus(result);
ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01}));
}
TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) {
int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
int32_t propSteering = toInt(VehicleProperty::PERF_STEERING_ANGLE);
int32_t areaId = 0;
getHardware()->updateSampleRate(propSpeed, areaId, 5);
ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500)))
<< "not enough events generated for speed";
getHardware()->updateSampleRate(propSteering, areaId, 10);
ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/10, milliseconds(1500)))
<< "not enough events generated for steering";
int64_t timestamp = elapsedRealtimeNano();
// Disable refreshing for propSpeed.
getHardware()->updateSampleRate(propSpeed, areaId, 0);
clearChangedProperties();
ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500)))
<< "should still receive steering events after disable polling for speed";
auto updatedValues = getChangedProperties();
for (auto& value : updatedValues) {
ASSERT_GE(value.timestamp, timestamp);
ASSERT_EQ(value.prop, propSteering);
ASSERT_EQ(value.areaId, areaId);
}
}
TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) {
float CELSIUS = static_cast<float>(toInt(VehicleUnit::CELSIUS));
float FAHRENHEIT = static_cast<float>(toInt(VehicleUnit::FAHRENHEIT));
VehiclePropValue floatArraySizeFour = {
.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {0, CELSIUS, 0, 0},
};
StatusCode status = setValue(floatArraySizeFour);
EXPECT_EQ(status, StatusCode::OK);
VehiclePropValue floatArraySizeZero = {
.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
};
status = setValue(floatArraySizeZero);
EXPECT_EQ(status, StatusCode::INVALID_ARG);
VehiclePropValue floatArraySizeFive = {
.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {0, CELSIUS, 0, 0, 0},
};
status = setValue(floatArraySizeFive);
EXPECT_EQ(status, StatusCode::INVALID_ARG);
VehiclePropValue invalidUnit = {
.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {0, 0, 0, 0},
};
status = setValue(floatArraySizeFive);
EXPECT_EQ(status, StatusCode::INVALID_ARG);
clearChangedProperties();
// Config array values from HVAC_TEMPERATURE_SET in DefaultProperties.json
auto configs = getHardware()->getAllPropertyConfigs();
VehiclePropConfig* hvacTemperatureSetConfig = nullptr;
for (auto& config : configs) {
if (config.prop == toInt(VehicleProperty::HVAC_TEMPERATURE_SET)) {
hvacTemperatureSetConfig = &config;
break;
}
}
EXPECT_NE(hvacTemperatureSetConfig, nullptr);
auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfig->configArray;
// The HVAC_TEMPERATURE_SET config array values are temperature values that have been multiplied
// by 10 and converted to integers. HVAC_TEMPERATURE_VALUE_SUGGESTION specifies the temperature
// values to be in the original floating point form so we divide by 10 and convert to float.
float minTempInCelsius = hvacTemperatureSetConfigArray[0] / 10.0f;
float maxTempInCelsius = hvacTemperatureSetConfigArray[1] / 10.0f;
float incrementInCelsius = hvacTemperatureSetConfigArray[2] / 10.0f;
float minTempInFahrenheit = hvacTemperatureSetConfigArray[3] / 10.0f;
float maxTempInFahrenheit = hvacTemperatureSetConfigArray[4] / 10.0f;
float incrementInFahrenheit = hvacTemperatureSetConfigArray[5] / 10.0f;
auto testCases = {
SetSpecialValueTestCase{
.name = "min_celsius_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius, CELSIUS, 0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius, CELSIUS,
minTempInCelsius,
minTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "min_fahrenheit_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit, FAHRENHEIT,
minTempInCelsius,
minTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "max_celsius_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius, CELSIUS, 0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius, CELSIUS,
maxTempInCelsius,
maxTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "max_fahrenheit_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit, FAHRENHEIT,
maxTempInCelsius,
maxTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "below_min_celsius_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius - 1, CELSIUS, 0,
0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius - 1, CELSIUS,
minTempInCelsius,
minTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "below_min_fahrenheit_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit - 1,
FAHRENHEIT, 0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit - 1,
FAHRENHEIT, minTempInCelsius,
minTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "above_max_celsius_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius + 1, CELSIUS, 0,
0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInCelsius + 1, CELSIUS,
maxTempInCelsius,
maxTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "above_max_fahrenheit_temperature",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit + 1,
FAHRENHEIT, 0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {maxTempInFahrenheit + 1,
FAHRENHEIT, maxTempInCelsius,
maxTempInFahrenheit},
},
},
},
SetSpecialValueTestCase{
.name = "inbetween_value_celsius",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInCelsius +
incrementInCelsius * 2.5f,
CELSIUS, 0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues =
{minTempInCelsius + incrementInCelsius * 2.5f,
CELSIUS,
minTempInCelsius + incrementInCelsius * 2,
minTempInFahrenheit +
incrementInFahrenheit * 2},
},
},
},
SetSpecialValueTestCase{
.name = "inbetween_value_fahrenheit",
.valuesToSet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues = {minTempInFahrenheit +
incrementInFahrenheit *
2.5f,
FAHRENHEIT, 0, 0},
},
},
.expectedValuesToGet =
{
VehiclePropValue{
.prop = toInt(
VehicleProperty::
HVAC_TEMPERATURE_VALUE_SUGGESTION),
.areaId = HVAC_ALL,
.value.floatValues =
{minTempInFahrenheit +
incrementInFahrenheit * 2.5f,
FAHRENHEIT,
minTempInCelsius + incrementInCelsius * 2,
minTempInFahrenheit +
incrementInFahrenheit * 2},
},
},
},
};
for (auto& tc : testCases) {
StatusCode status = setValue(tc.valuesToSet[0]);
EXPECT_EQ(status, StatusCode::OK);
auto events = getChangedProperties();
EXPECT_EQ(events.size(), static_cast<size_t>(1));
events[0].timestamp = 0;
EXPECT_EQ(events[0], tc.expectedValuesToGet[0]);
clearChangedProperties();
}
}
} // namespace fake
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android