From bc352866fecfcb64f7c173d8c6e9c129bc1b159b Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Sat, 9 Apr 2022 00:29:29 +0000 Subject: [PATCH 1/2] Support fake data generator in FakeVehicleHardware. Support dump command 'genfakedata' in FakeVehicleHardware. Client could use this command to generate fake events. This is used in vehiclehal integration test. Test: atest FakeVehicleHardwareTest Bug: 199314530 Change-Id: Id7abcf289debe22f29f4299cc93417ac51bc1a3b --- .../GeneratorHub/include/GeneratorHub.h | 4 +- .../include/JsonFakeValueGenerator.h | 3 + .../GeneratorHub/src/GeneratorHub.cpp | 6 +- .../src/JsonFakeValueGenerator.cpp | 4 + .../hardware/include/FakeVehicleHardware.h | 12 + .../hardware/src/FakeVehicleHardware.cpp | 179 +++++++++++- .../impl/fake_impl/hardware/test/Android.bp | 6 + .../hardware/test/FakeVehicleHardwareTest.cpp | 254 +++++++++++++++++- .../impl/fake_impl/hardware/test/prop.json | 26 ++ 9 files changed, 485 insertions(+), 9 deletions(-) create mode 100644 automotive/vehicle/aidl/impl/fake_impl/hardware/test/prop.json diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h index 9f112ae794..f96b6ecbd4 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h +++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/GeneratorHub.h @@ -54,8 +54,8 @@ class GeneratorHub { void registerGenerator(int32_t generatorId, std::unique_ptr generator); // Unregister a generator with the generatorId. If no registered generator is found, this - // function does nothing. - void unregisterGenerator(int32_t generatorId); + // function does nothing. Returns true if the generator is unregistered. + bool unregisterGenerator(int32_t generatorId); private: struct VhalEvent { diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h index d421ac525d..ab6e2fefb1 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h +++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h @@ -53,6 +53,9 @@ class JsonFakeValueGenerator : public FakeValueGenerator { const std::vector& getAllEvents(); + // Whether there are events left to replay for this generator. + bool hasNext(); + private: size_t mEventIndex = 0; std::vector mEvents; diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp index 0c182d9c2b..1690c78ef9 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/GeneratorHub.cpp @@ -58,13 +58,15 @@ void GeneratorHub::registerGenerator(int32_t id, std::unique_ptr lockGuard(mGeneratorsLock); - mGenerators.erase(id); + removed = mGenerators.erase(id); } mCond.notify_one(); ALOGI("%s: Unregistered generator, id: %d", __func__, id); + return removed; } void GeneratorHub::run() { diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp index d4d52a5849..2ba55337e0 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp @@ -241,6 +241,10 @@ std::optional JsonFakeValueGenerator::nextEvent() { return generatedValue; } +bool JsonFakeValueGenerator::hasNext() { + return mNumOfIterations != 0 && mEvents.size() > 0; +} + } // namespace fake } // namespace vehicle } // namespace automotive diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h index 34b2b2454f..a4fc619817 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -132,6 +134,8 @@ class FakeVehicleHardware : public IVehicleHardware { const std::unique_ptr mFakeUserHal; // RecurrentTimer is thread-safe. std::unique_ptr mRecurrentTimer; + // GeneratorHub is thread-safe. + std::unique_ptr mGeneratorHub; std::mutex mLock; std::unique_ptr mOnPropertyChangeCallback GUARDED_BY(mLock); std::unique_ptr mOnPropertySetErrorCallback GUARDED_BY(mLock); @@ -207,6 +211,14 @@ class FakeVehicleHardware : public IVehicleHardware { const aidl::android::hardware::automotive::vehicle::GetValueRequest& request); aidl::android::hardware::automotive::vehicle::SetValueResult handleSetValueRequest( const aidl::android::hardware::automotive::vehicle::SetValueRequest& request); + + std::string genFakeDataCommand(const std::vector& options); + + static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp( + aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action, + int32_t keyCode, int32_t targetDisplay); + static std::string genFakeDataHelp(); + static std::string parseErrMsg(std::string fieldName, std::string value, std::string type); }; } // namespace fake diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp index 7b3de586c8..1b1969af31 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ 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::VehiclePropertyGroup; @@ -140,6 +142,8 @@ FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr v mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)), mFakeUserHal(new FakeUserHal(mValuePool)), mRecurrentTimer(new RecurrentTimer()), + mGeneratorHub(new GeneratorHub( + [this](const VehiclePropValue& value) { onValueChangeCallback(value); })), mPendingGetValueRequests(this), mPendingSetValueRequests(this) { init(); @@ -148,6 +152,7 @@ FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr v FakeVehicleHardware::~FakeVehicleHardware() { mPendingGetValueRequests.stop(); mPendingSetValueRequests.stop(); + mGeneratorHub.reset(); } void FakeVehicleHardware::init() { @@ -583,12 +588,181 @@ DumpResult FakeVehicleHardware::dump(const std::vector& options) { } else { result.buffer = mFakeUserHal->dump(options[1]); } + } else if (EqualsIgnoreCase(option, "--genfakedata")) { + result.buffer = genFakeDataCommand(options); } else { result.buffer = StringPrintf("Invalid option: %s\n", option.c_str()); } return result; } +std::string FakeVehicleHardware::genFakeDataHelp() { + return R"( +Generate Fake Data Usage: +--genfakedata --startlinear [propID] [mValue] [cValue] [dispersion] [increment] [interval]: " +Start a linear generator that generates event with floatValue within range: +[mValue - disperson, mValue + dispersion]. +propID(int32): ID for the property to generate event for. +mValue(float): The middle of the possible values for the property. +cValue(float): The start value for the property, must be within the range. +dispersion(float): The range the value can change. +increment(float): The step the value would increase by for each generated event, +if exceed the range, the value would loop back. +interval(int64): The interval in nanoseconds the event would generate by. + +--genfakedata --stoplinear [propID(int32)]: Stop a linear generator + +--genfakedata --startjson [jsonFilePath] [repetition]: +Start a json generator that would generate events according to a JSON file. +jsonFilePath(string): The path to a JSON file. The JSON content must be in the format of +[{ + "timestamp": 1000000, + "areaId": 0, + "value": 8, + "prop": 289408000 +}, {...}] +Each event in the JSON file would be generated by the same interval their timestamp is relative to +the first event's timestamp. +repetition(int32, optional): how many iterations the events would be generated. If it is not +provided, it would iterate indefinitely. + +--genfakedata --stopjson [jsonFilePath(string)]: Stop a json generator + +--genfakedata --keypress [keyCode(int32)] [display[int32]]: Generate key press + +)"; +} + +std::string FakeVehicleHardware::parseErrMsg(std::string fieldName, std::string value, + std::string type) { + return StringPrintf("failed to parse %s as %s: \"%s\"\n%s", fieldName.c_str(), type.c_str(), + value.c_str(), genFakeDataHelp().c_str()); +} + +std::string FakeVehicleHardware::genFakeDataCommand(const std::vector& options) { + if (options.size() < 2) { + return "No subcommand specified for genfakedata\n" + genFakeDataHelp(); + } + + std::string command = options[1]; + if (command == "--startlinear") { + // --genfakedata --startlinear [propID(int32)] [middleValue(float)] + // [currentValue(float)] [dispersion(float)] [increment(float)] [interval(int64)] + if (options.size() != 8) { + return "incorrect argument count, need 8 arguments for --genfakedata --startlinear\n" + + genFakeDataHelp(); + } + int32_t propId; + float middleValue; + float currentValue; + float dispersion; + float increment; + int64_t interval; + if (!android::base::ParseInt(options[2], &propId)) { + return parseErrMsg("propId", options[2], "int"); + } + if (!android::base::ParseFloat(options[3], &middleValue)) { + return parseErrMsg("middleValue", options[3], "float"); + } + if (!android::base::ParseFloat(options[4], ¤tValue)) { + return parseErrMsg("currentValue", options[4], "float"); + } + if (!android::base::ParseFloat(options[5], &dispersion)) { + return parseErrMsg("dispersion", options[5], "float"); + } + if (!android::base::ParseFloat(options[6], &increment)) { + return parseErrMsg("increment", options[6], "float"); + } + if (!android::base::ParseInt(options[7], &interval)) { + return parseErrMsg("interval", options[7], "int"); + } + auto generator = std::make_unique( + propId, middleValue, currentValue, dispersion, increment, interval); + mGeneratorHub->registerGenerator(propId, std::move(generator)); + return "Linear event generator started successfully"; + } else if (command == "--stoplinear") { + // --genfakedata --stoplinear [propID(int32)] + if (options.size() != 3) { + return "incorrect argument count, need 3 arguments for --genfakedata --stoplinear\n" + + genFakeDataHelp(); + } + int32_t propId; + if (!android::base::ParseInt(options[2], &propId)) { + return parseErrMsg("propId", options[2], "int"); + } + if (mGeneratorHub->unregisterGenerator(propId)) { + return "Linear event generator stopped successfully"; + } + return StringPrintf("No linear event generator found for property: %d", propId); + } else if (command == "--startjson") { + // --genfakedata --startjson [jsonFilePath(string)] [repetition(int32)(optional)] + if (options.size() != 3 && options.size() != 4) { + return "incorrect argument count, need 3 or 4 arguments for --genfakedata " + "--startjson\n"; + } + std::string fileName = options[2]; + int32_t cookie = std::hash()(fileName); + // Iterate infinitely if repetition number is not provided + int32_t repetition = -1; + if (options.size() == 4) { + if (!android::base::ParseInt(options[3], &repetition)) { + return parseErrMsg("repetition", options[3], "int"); + } + } + auto generator = std::make_unique(fileName, repetition); + if (!generator->hasNext()) { + return "invalid JSON file, no events"; + } + mGeneratorHub->registerGenerator(cookie, std::move(generator)); + return "JSON event generator started successfully"; + } else if (command == "--stopjson") { + // --genfakedata --stopjson [jsonFilePath(string)] + if (options.size() != 3) { + return "incorrect argument count, need 3 arguments for --genfakedata --stopjson\n"; + } + std::string fileName = options[2]; + int32_t cookie = std::hash()(fileName); + if (mGeneratorHub->unregisterGenerator(cookie)) { + return "JSON event generator stopped successfully"; + } else { + return StringPrintf("No JSON event generator found for file: %s", fileName.c_str()); + } + } else if (command == "--keypress") { + int32_t keyCode; + int32_t display; + // --genfakedata --keypress [keyCode(int32)] [display[int32]] + if (options.size() != 4) { + return "incorrect argument count, need 4 arguments for --genfakedata --keypress\n"; + } + if (!android::base::ParseInt(options[2], &keyCode)) { + return parseErrMsg("keyCode", options[2], "int"); + } + if (!android::base::ParseInt(options[3], &display)) { + return parseErrMsg("display", options[3], "int"); + } + // Send back to HAL + onValueChangeCallback( + createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display)); + onValueChangeCallback( + createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display)); + return "keypress event generated successfully"; + } + + return StringPrintf("Unknown command: \"%s\"\n%s", command.c_str(), genFakeDataHelp().c_str()); +} + +VehiclePropValue FakeVehicleHardware::createHwInputKeyProp(VehicleHwKeyInputAction action, + int32_t keyCode, int32_t targetDisplay) { + VehiclePropValue value = { + .prop = toInt(VehicleProperty::HW_KEY_INPUT), + .areaId = 0, + .timestamp = elapsedRealtimeNano(), + .status = VehiclePropertyStatus::AVAILABLE, + .value.int32Values = {toInt(action), keyCode, targetDisplay}, + }; + return value; +} + std::string FakeVehicleHardware::dumpHelp() { return "Usage: \n\n" "[no args]: dumps (id and value) all supported properties \n" @@ -600,9 +774,8 @@ std::string FakeVehicleHardware::dumpHelp() { "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. " "Notice that the string, bytes and area value can be set just once, while the other can" " have multiple values (so they're used in the respective array), " - "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n\n" - "Fake user HAL usage: \n" + - mFakeUserHal->showDumpHelp(); + "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n\n" + + genFakeDataHelp() + "Fake user HAL usage: \n" + mFakeUserHal->showDumpHelp(); } std::string FakeVehicleHardware::dumpAllProperties() { diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp index 90d1516a21..cfd65770dc 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/Android.bp @@ -42,6 +42,7 @@ cc_test { ], data: [ ":FakeVehicleHardwareTestOverrideJson", + ":FakeVehicleHardwareTestPropJson", ], defaults: ["VehicleHalDefaults"], test_suites: ["device-tests"], @@ -51,3 +52,8 @@ filegroup { name: "FakeVehicleHardwareTestOverrideJson", srcs: ["override/*"], } + +filegroup { + name: "FakeVehicleHardwareTestPropJson", + srcs: ["prop.json"], +} diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp index 3e8f634ab7..f3feea6b46 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp @@ -53,6 +53,7 @@ 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; @@ -64,6 +65,7 @@ using ::android::base::unexpected; using ::testing::ContainerEq; using ::testing::ContainsRegex; using ::testing::Eq; +using ::testing::HasSubstr; using ::testing::WhenSortedBy; using std::chrono::milliseconds; @@ -87,6 +89,7 @@ class FakeVehicleHardwareTestHelper { class FakeVehicleHardwareTest : public ::testing::Test { protected: void SetUp() override { + mHardware = std::make_unique(); auto callback = std::make_unique( [this](const std::vector& values) { onPropertyChangeEvent(values); @@ -98,7 +101,13 @@ class FakeVehicleHardwareTest : public ::testing::Test { [this](std::vector results) { onGetValues(results); }); } - FakeVehicleHardware* getHardware() { return &mHardware; } + 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(); } StatusCode setValues(const std::vector& requests) { { @@ -251,6 +260,14 @@ class FakeVehicleHardwareTest : public ::testing::Test { return mChangedProperties; } + bool waitForChangedProperties(size_t count, milliseconds timeout) { + std::unique_lock 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{ @@ -270,6 +287,15 @@ class FakeVehicleHardwareTest : public ::testing::Test { mChangedProperties.clear(); } + size_t getEventCount(int32_t propId, int32_t areaId) { + PropIdAreaId propIdAreaId{ + .propId = propId, + .areaId = areaId, + }; + std::scoped_lock lockGuard(mLock); + return mEventCount[propIdAreaId]; + } + static void addSetValueRequest(std::vector& requests, std::vector& expectedResults, int64_t requestId, const VehiclePropValue& value, StatusCode expectedStatus) { @@ -332,7 +358,7 @@ class FakeVehicleHardwareTest : public ::testing::Test { } mPropValueCmp; private: - FakeVehicleHardware mHardware; + std::unique_ptr mHardware; std::shared_ptr mSetValuesCallback; std::shared_ptr mGetValuesCallback; std::condition_variable mCv; @@ -1606,6 +1632,230 @@ TEST_F(FakeVehicleHardwareTest, SetComplexPropTest) { ASSERT_EQ(3.402823466E+38f, value.value.floatValues[2]); } +struct OptionsTestCase { + std::string name; + std::vector options; + std::string expectMsg; +}; + +class FakeVehicleHardwareOptionsTest : public FakeVehicleHardwareTest, + public testing::WithParamInterface {}; + +std::vector 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", "file", "0.1"}, + "failed to parse repetition as int: \"0.1\""}, + {"genfakedata_startjson_invalid_json_file", + {"--genfakedata", "--startjson", "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\""}}; +} + +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& 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 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(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 + "/" + filename; +} + +TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) { + std::vector options = {"--genfakedata", "--startjson", + 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, testDebugGenFakeDataJsonInvalidFile) { + std::vector options = {"--genfakedata", "--startjson", + 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 options = {"--genfakedata", "--startjson", + getTestFilePath("prop.json")}; + + DumpResult result = getHardware()->dump(options); + + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_THAT(result.buffer, HasSubstr("successfully")); + + result = getHardware()->dump({"--genfakedata", "--stopjson", getTestFilePath("prop.json")}); + + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_THAT(result.buffer, HasSubstr("successfully")); +} + +TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStopInvalidFile) { + // No iteration number provided, would loop indefinitely. + std::vector options = {"--genfakedata", "--startjson", + getTestFilePath("prop.json")}; + + DumpResult result = getHardware()->dump(options); + + ASSERT_FALSE(result.callerShouldDumpState); + ASSERT_THAT(result.buffer, HasSubstr("successfully")); + + result = getHardware()->dump({"--genfakedata", "--stopjson", getTestFilePath("prop1.json")}); + + 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 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, testGetEchoReverseBytes) { ASSERT_EQ(setValue(VehiclePropValue{ .prop = ECHO_REVERSE_BYTES, diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/prop.json b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/prop.json new file mode 100644 index 0000000000..7123a002cc --- /dev/null +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/prop.json @@ -0,0 +1,26 @@ +[ + { + "timestamp": 1000000, + "areaId": 0, + "value": 8, + "prop": 289408000 + }, + { + "timestamp": 2000000, + "areaId": 0, + "value": 4, + "prop": 289408000 + }, + { + "timestamp": 3000000, + "areaId": 0, + "value": 16, + "prop": 289408000 + }, + { + "timestamp": 4000000, + "areaId": 0, + "value": 10, + "prop": 289408000 + } +] From 86233e53b1a0ab2a73d0a929511c6f41ff827a1b Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Fri, 15 Apr 2022 15:27:45 -0700 Subject: [PATCH 2/2] Add more debug options to FakeVehicleHardware. Add more debug options to FakeVehicleHardware that includes save property values and restore property values. Get property with arguments. Provide JSON content to fake event generator. Test: atest FakeVehicleHardwareTest Bug: 199314530 Change-Id: I467b3aff1b7178f8716831e9c501d8199c35c359 --- .../include/JsonFakeValueGenerator.h | 6 +- .../src/JsonFakeValueGenerator.cpp | 24 +- .../hardware/include/FakeVehicleHardware.h | 12 +- .../hardware/src/FakeVehicleHardware.cpp | 301 ++++++++++++++---- .../hardware/test/FakeVehicleHardwareTest.cpp | 125 +++++++- 5 files changed, 385 insertions(+), 83 deletions(-) diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h index ab6e2fefb1..42b1bd3c6c 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h +++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/include/JsonFakeValueGenerator.h @@ -45,6 +45,9 @@ class JsonFakeValueGenerator : public FakeValueGenerator { // Create a new JSON fake value generator using the specified JSON file path. All the events // in the JSON file would be generated once. explicit JsonFakeValueGenerator(const std::string& path); + // Create a new JSON fake value generator using the JSON content. The first argument is just + // used to differentiate this function with the one that takes path as input. + explicit JsonFakeValueGenerator(bool unused, const std::string& content, int32_t iteration); ~JsonFakeValueGenerator() = default; @@ -63,7 +66,8 @@ class JsonFakeValueGenerator : public FakeValueGenerator { int32_t mNumOfIterations = 0; void setBit(std::vector& bytes, size_t idx); - void init(const std::string& path, int32_t iteration); + void initWithPath(const std::string& path, int32_t iteration); + void initWithStream(std::istream& is, int32_t iteration); }; } // namespace fake diff --git a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp index 2ba55337e0..cb42317195 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/GeneratorHub/src/JsonFakeValueGenerator.cpp @@ -173,12 +173,11 @@ std::vector parseFakeValueJson(std::istream& is) { } // namespace -JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path) { - init(path, 1); -} +JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path) + : JsonFakeValueGenerator(path, /*iteration=*/1) {} JsonFakeValueGenerator::JsonFakeValueGenerator(const std::string& path, int32_t iteration) { - init(path, iteration); + initWithPath(path, iteration); } JsonFakeValueGenerator::JsonFakeValueGenerator(const VehiclePropValue& request) { @@ -186,16 +185,26 @@ JsonFakeValueGenerator::JsonFakeValueGenerator(const VehiclePropValue& request) // Iterate infinitely if iteration number is not provided int32_t numOfIterations = v.int32Values.size() < 2 ? -1 : v.int32Values[1]; - init(v.stringValue, numOfIterations); + initWithPath(v.stringValue, numOfIterations); } -void JsonFakeValueGenerator::init(const std::string& path, int32_t iteration) { +JsonFakeValueGenerator::JsonFakeValueGenerator([[maybe_unused]] bool unused, + const std::string& content, int32_t iteration) { + std::istringstream iss(content); + initWithStream(iss, iteration); +} + +void JsonFakeValueGenerator::initWithPath(const std::string& path, int32_t iteration) { std::ifstream ifs(path); if (!ifs) { ALOGE("%s: couldn't open %s for parsing.", __func__, path.c_str()); return; } - mEvents = parseFakeValueJson(ifs); + initWithStream(ifs, iteration); +} + +void JsonFakeValueGenerator::initWithStream(std::istream& is, int32_t iteration) { + mEvents = parseFakeValueJson(is); mNumOfIterations = iteration; } @@ -235,7 +244,6 @@ std::optional JsonFakeValueGenerator::nextEvent() { mNumOfIterations--; } } - generatedValue.timestamp = mLastEventTimestamp; return generatedValue; diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h index a4fc619817..8cc19b12f8 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h @@ -141,6 +141,8 @@ class FakeVehicleHardware : public IVehicleHardware { std::unique_ptr mOnPropertySetErrorCallback GUARDED_BY(mLock); std::unordered_map, PropIdAreaIdHash> mRecurrentActions GUARDED_BY(mLock); + std::unordered_map + mSavedProps GUARDED_BY(mLock); // PendingRequestHandler is thread-safe. mutable PendingRequestHandler @@ -160,6 +162,10 @@ class FakeVehicleHardware : public IVehicleHardware { void maybeOverrideProperties(const char* overrideDir); // Override the properties using config files in 'overrideDir'. void overrideProperties(const char* overrideDir); + // Function to be called when a value change event comes from vehicle bus. In our fake + // implementation, this function is only called during "--inject-event" dump command. + void eventFromVehicleBus( + const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value); VhalResult maybeSetSpecialValue( const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value, @@ -188,6 +194,10 @@ class FakeVehicleHardware : public IVehicleHardware { std::string dumpListProperties(); std::string dumpSpecificProperty(const std::vector& options); std::string dumpSetProperties(const std::vector& options); + std::string dumpGetPropertyWithArg(const std::vector& options); + std::string dumpSaveProperty(const std::vector& options); + std::string dumpRestoreProperty(const std::vector& options); + std::string dumpInjectEvent(const std::vector& options); template android::base::Result safelyParseInt(int index, const std::string& s) { @@ -202,7 +212,7 @@ class FakeVehicleHardware : public IVehicleHardware { std::vector getOptionValues(const std::vector& options, size_t* index); android::base::Result - parseSetPropOptions(const std::vector& options); + parsePropOptions(const std::vector& options); android::base::Result> parseHexString(const std::string& s); android::base::Result checkArgumentsSize(const std::vector& options, diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp index 1b1969af31..a7e986b9ca 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -88,7 +89,9 @@ const std::unordered_set SET_PROP_OPTIONS = { // bytes in hex format, e.g. 0xDEADBEEF. "-b", // Area id in integer. - "-a"}; + "-a", + // Timestamp in int64. + "-t"}; } // namespace @@ -143,7 +146,7 @@ FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr v mFakeUserHal(new FakeUserHal(mValuePool)), mRecurrentTimer(new RecurrentTimer()), mGeneratorHub(new GeneratorHub( - [this](const VehiclePropValue& value) { onValueChangeCallback(value); })), + [this](const VehiclePropValue& value) { eventFromVehicleBus(value); })), mPendingGetValueRequests(this), mPendingSetValueRequests(this) { init(); @@ -580,8 +583,16 @@ DumpResult FakeVehicleHardware::dump(const std::vector& options) { result.buffer = dumpListProperties(); } else if (EqualsIgnoreCase(option, "--get")) { result.buffer = dumpSpecificProperty(options); + } else if (EqualsIgnoreCase(option, "--getWithArg")) { + result.buffer = dumpGetPropertyWithArg(options); } else if (EqualsIgnoreCase(option, "--set")) { result.buffer = dumpSetProperties(options); + } else if (EqualsIgnoreCase(option, "--save-prop")) { + result.buffer = dumpSaveProperty(options); + } else if (EqualsIgnoreCase(option, "--restore-prop")) { + result.buffer = dumpRestoreProperty(options); + } else if (EqualsIgnoreCase(option, "--inject-event")) { + result.buffer = dumpInjectEvent(options); } else if (EqualsIgnoreCase(option, kUserHalDumpOption)) { if (options.size() == 1) { result.buffer = mFakeUserHal->showDumpHelp(); @@ -612,8 +623,8 @@ interval(int64): The interval in nanoseconds the event would generate by. --genfakedata --stoplinear [propID(int32)]: Stop a linear generator ---genfakedata --startjson [jsonFilePath] [repetition]: -Start a json generator that would generate events according to a JSON file. +--genfakedata --startjson --path [jsonFilePath] [repetition]: +Start a JSON generator that would generate events according to a JSON file. jsonFilePath(string): The path to a JSON file. The JSON content must be in the format of [{ "timestamp": 1000000, @@ -626,9 +637,11 @@ the first event's timestamp. repetition(int32, optional): how many iterations the events would be generated. If it is not provided, it would iterate indefinitely. ---genfakedata --stopjson [jsonFilePath(string)]: Stop a json generator +--genfakedata --startjson --content [jsonContent]: Start a JSON generator using the content. ---genfakedata --keypress [keyCode(int32)] [display[int32]]: Generate key press +--genfakedata --stopjson [generatorID(string)]: Stop a JSON generator. + +--genfakedata --keypress [keyCode(int32)] [display[int32]]: Generate key press. )"; } @@ -695,37 +708,51 @@ std::string FakeVehicleHardware::genFakeDataCommand(const std::vector()(fileName); // Iterate infinitely if repetition number is not provided int32_t repetition = -1; - if (options.size() == 4) { - if (!android::base::ParseInt(options[3], &repetition)) { - return parseErrMsg("repetition", options[3], "int"); + if (options.size() == 5) { + if (!android::base::ParseInt(options[4], &repetition)) { + return parseErrMsg("repetition", options[4], "int"); } } - auto generator = std::make_unique(fileName, repetition); - if (!generator->hasNext()) { - return "invalid JSON file, no events"; + std::unique_ptr generator; + if (options[2] == "--path") { + const std::string& fileName = options[3]; + generator = std::make_unique(fileName, repetition); + if (!generator->hasNext()) { + return "invalid JSON file, no events"; + } + } else if (options[2] == "--content") { + const std::string& content = options[3]; + generator = + std::make_unique(/*unused=*/true, content, repetition); + if (!generator->hasNext()) { + return "invalid JSON content, no events"; + } } + int32_t cookie = std::hash()(options[3]); mGeneratorHub->registerGenerator(cookie, std::move(generator)); - return "JSON event generator started successfully"; + return StringPrintf("JSON event generator started successfully, ID: %" PRId32, cookie); } else if (command == "--stopjson") { - // --genfakedata --stopjson [jsonFilePath(string)] + // --genfakedata --stopjson [generatorID(string)] if (options.size() != 3) { return "incorrect argument count, need 3 arguments for --genfakedata --stopjson\n"; } - std::string fileName = options[2]; - int32_t cookie = std::hash()(fileName); + int32_t cookie; + if (!android::base::ParseInt(options[2], &cookie)) { + return parseErrMsg("cookie", options[2], "int"); + } if (mGeneratorHub->unregisterGenerator(cookie)) { return "JSON event generator stopped successfully"; } else { - return StringPrintf("No JSON event generator found for file: %s", fileName.c_str()); + return StringPrintf("No JSON event generator found for ID: %s", options[2].c_str()); } } else if (command == "--keypress") { int32_t keyCode; @@ -763,18 +790,29 @@ VehiclePropValue FakeVehicleHardware::createHwInputKeyProp(VehicleHwKeyInputActi return value; } +void FakeVehicleHardware::eventFromVehicleBus(const VehiclePropValue& value) { + mServerSidePropStore->writeValue(mValuePool->obtain(value)); +} + std::string FakeVehicleHardware::dumpHelp() { return "Usage: \n\n" "[no args]: dumps (id and value) all supported properties \n" "--help: shows this help\n" "--list: lists the ids of all supported properties\n" - "--get [PROP2] [PROPN]: dumps the value of specific properties \n" - "--set [-i INT_VALUE [INT_VALUE ...]] [-i64 INT64_VALUE [INT64_VALUE ...]] " - "[-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] " - "[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. " + "--get [PROP2] [PROPN]: dumps the value of specific properties. \n" + "--getWithArg [ValueArguments]: gets the value for a specific property with " + "arguments. \n" + "--set [ValueArguments]: sets the value of property PROP. \n" + "--save-prop [-a AREA_ID]: saves the current value for PROP, integration test" + " that modifies prop value must call this before test and restore-prop after test. \n" + "--restore-prop [-a AREA_ID]: restores a previously saved property value. \n" + "--inject-event [ValueArguments]: inject a property update event from car\n\n" + "ValueArguments are in the format of [-i INT_VALUE [INT_VALUE ...]] " + "[-i64 INT64_VALUE [INT64_VALUE ...]] [-f FLOAT_VALUE [FLOAT_VALUE ...]] [-s STR_VALUE] " + "[-b BYTES_VALUE] [-a AREA_ID].\n" "Notice that the string, bytes and area value can be set just once, while the other can" " have multiple values (so they're used in the respective array), " - "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n\n" + + "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n" + genFakeDataHelp() + "Fake user HAL usage: \n" + mFakeUserHal->showDumpHelp(); } @@ -893,10 +931,11 @@ std::vector FakeVehicleHardware::getOptionValues( return std::move(values); } -Result FakeVehicleHardware::parseSetPropOptions( +Result FakeVehicleHardware::parsePropOptions( const std::vector& options) { // Options format: - // --set PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a] + // --set/get/inject-event PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] + // [-b b1 b2...] [-a a] [-t timestamp] size_t optionIndex = 1; auto result = safelyParseInt(optionIndex, options[optionIndex]); if (!result.ok()) { @@ -910,83 +949,98 @@ Result FakeVehicleHardware::parseSetPropOptions( std::unordered_set parsedOptions; while (optionIndex < options.size()) { - std::string type = options[optionIndex]; + std::string argType = options[optionIndex]; optionIndex++; + size_t currentIndex = optionIndex; - std::vector values = getOptionValues(options, &optionIndex); - if (parsedOptions.find(type) != parsedOptions.end()) { - return Error() << StringPrintf("Duplicate \"%s\" options\n", type.c_str()); + std::vector argValues = getOptionValues(options, &optionIndex); + if (parsedOptions.find(argType) != parsedOptions.end()) { + return Error() << StringPrintf("Duplicate \"%s\" options\n", argType.c_str()); } - parsedOptions.insert(type); - if (EqualsIgnoreCase(type, "-i")) { - if (values.size() == 0) { + parsedOptions.insert(argType); + size_t argValuesSize = argValues.size(); + if (EqualsIgnoreCase(argType, "-i")) { + if (argValuesSize == 0) { return Error() << "No values specified when using \"-i\"\n"; } - prop.value.int32Values.resize(values.size()); - for (size_t i = 0; i < values.size(); i++) { - auto int32Result = safelyParseInt(currentIndex + i, values[i]); + prop.value.int32Values.resize(argValuesSize); + for (size_t i = 0; i < argValuesSize; i++) { + auto int32Result = safelyParseInt(currentIndex + i, argValues[i]); if (!int32Result.ok()) { return Error() << StringPrintf("Value: \"%s\" is not a valid int: %s\n", - values[i].c_str(), getErrorMsg(int32Result).c_str()); + argValues[i].c_str(), getErrorMsg(int32Result).c_str()); } prop.value.int32Values[i] = int32Result.value(); } - } else if (EqualsIgnoreCase(type, "-i64")) { - if (values.size() == 0) { + } else if (EqualsIgnoreCase(argType, "-i64")) { + if (argValuesSize == 0) { return Error() << "No values specified when using \"-i64\"\n"; } - prop.value.int64Values.resize(values.size()); - for (size_t i = 0; i < values.size(); i++) { - auto int64Result = safelyParseInt(currentIndex + i, values[i]); + prop.value.int64Values.resize(argValuesSize); + for (size_t i = 0; i < argValuesSize; i++) { + auto int64Result = safelyParseInt(currentIndex + i, argValues[i]); if (!int64Result.ok()) { return Error() << StringPrintf("Value: \"%s\" is not a valid int64: %s\n", - values[i].c_str(), getErrorMsg(int64Result).c_str()); + argValues[i].c_str(), getErrorMsg(int64Result).c_str()); } prop.value.int64Values[i] = int64Result.value(); } - } else if (EqualsIgnoreCase(type, "-f")) { - if (values.size() == 0) { + } else if (EqualsIgnoreCase(argType, "-f")) { + if (argValuesSize == 0) { return Error() << "No values specified when using \"-f\"\n"; } - prop.value.floatValues.resize(values.size()); - for (size_t i = 0; i < values.size(); i++) { - auto floatResult = safelyParseFloat(currentIndex + i, values[i]); + prop.value.floatValues.resize(argValuesSize); + for (size_t i = 0; i < argValuesSize; i++) { + auto floatResult = safelyParseFloat(currentIndex + i, argValues[i]); if (!floatResult.ok()) { return Error() << StringPrintf("Value: \"%s\" is not a valid float: %s\n", - values[i].c_str(), getErrorMsg(floatResult).c_str()); + argValues[i].c_str(), getErrorMsg(floatResult).c_str()); } prop.value.floatValues[i] = floatResult.value(); } - } else if (EqualsIgnoreCase(type, "-s")) { - if (values.size() != 1) { + } else if (EqualsIgnoreCase(argType, "-s")) { + if (argValuesSize != 1) { return Error() << "Expect exact one value when using \"-s\"\n"; } - prop.value.stringValue = values[0]; - } else if (EqualsIgnoreCase(type, "-b")) { - if (values.size() != 1) { + prop.value.stringValue = argValues[0]; + } else if (EqualsIgnoreCase(argType, "-b")) { + if (argValuesSize != 1) { return Error() << "Expect exact one value when using \"-b\"\n"; } - auto bytesResult = parseHexString(values[0]); + auto bytesResult = parseHexString(argValues[0]); if (!bytesResult.ok()) { return Error() << StringPrintf("value: \"%s\" is not a valid hex string: %s\n", - values[0].c_str(), getErrorMsg(bytesResult).c_str()); + argValues[0].c_str(), + getErrorMsg(bytesResult).c_str()); } prop.value.byteValues = std::move(bytesResult.value()); - } else if (EqualsIgnoreCase(type, "-a")) { - if (values.size() != 1) { + } else if (EqualsIgnoreCase(argType, "-a")) { + if (argValuesSize != 1) { return Error() << "Expect exact one value when using \"-a\"\n"; } - auto int32Result = safelyParseInt(currentIndex, values[0]); + auto int32Result = safelyParseInt(currentIndex, argValues[0]); if (!int32Result.ok()) { return Error() << StringPrintf("Area ID: \"%s\" is not a valid int: %s\n", - values[0].c_str(), getErrorMsg(int32Result).c_str()); + argValues[0].c_str(), + getErrorMsg(int32Result).c_str()); } prop.areaId = int32Result.value(); + } else if (EqualsIgnoreCase(argType, "-t")) { + if (argValuesSize != 1) { + return Error() << "Expect exact one value when using \"-t\"\n"; + } + auto int64Result = safelyParseInt(currentIndex, argValues[0]); + if (!int64Result.ok()) { + return Error() << StringPrintf("Timestamp: \"%s\" is not a valid int64: %s\n", + argValues[0].c_str(), + getErrorMsg(int64Result).c_str()); + } + prop.timestamp = int64Result.value(); } else { - return Error() << StringPrintf("Unknown option: %s\n", type.c_str()); + return Error() << StringPrintf("Unknown option: %s\n", argType.c_str()); } } @@ -998,7 +1052,7 @@ std::string FakeVehicleHardware::dumpSetProperties(const std::vector& options) { + if (auto result = checkArgumentsSize(options, 3); !result.ok()) { + return getErrorMsg(result); + } + + auto parseResult = parsePropOptions(options); + if (!parseResult.ok()) { + return getErrorMsg(parseResult); + } + VehiclePropValue prop = std::move(parseResult.value()); + ALOGD("Dump: Getting property: %s", prop.toString().c_str()); + + bool isSpecialValue = false; + auto result = maybeGetSpecialValue(prop, &isSpecialValue); + + if (!isSpecialValue) { + result = mServerSidePropStore->readValue(prop); + } + + if (!result.ok()) { + return StringPrintf("failed to read property value: %d, error: %s, code: %d\n", prop.prop, + getErrorMsg(result).c_str(), getIntErrorCode(result)); + } + return StringPrintf("Get property result: %s\n", result.value()->toString().c_str()); +} + +std::string FakeVehicleHardware::dumpSaveProperty(const std::vector& options) { + // Format: --save-prop PROP [-a areaID] + if (auto result = checkArgumentsSize(options, 2); !result.ok()) { + return getErrorMsg(result); + } + + auto parseResult = parsePropOptions(options); + if (!parseResult.ok()) { + return getErrorMsg(parseResult); + } + // We are only using the prop and areaId option. + VehiclePropValue value = std::move(parseResult.value()); + int32_t propId = value.prop; + int32_t areaId = value.areaId; + + auto readResult = mServerSidePropStore->readValue(value); + if (!readResult.ok()) { + return StringPrintf("Failed to save current property value, error: %s", + getErrorMsg(readResult).c_str()); + } + + std::scoped_lock lockGuard(mLock); + mSavedProps[PropIdAreaId{ + .propId = propId, + .areaId = areaId, + }] = std::move(readResult.value()); + + return StringPrintf("Property: %" PRId32 ", areaID: %" PRId32 " saved", propId, areaId); +} + +std::string FakeVehicleHardware::dumpRestoreProperty(const std::vector& options) { + // Format: --restore-prop PROP [-a areaID] + if (auto result = checkArgumentsSize(options, 2); !result.ok()) { + return getErrorMsg(result); + } + + auto parseResult = parsePropOptions(options); + if (!parseResult.ok()) { + return getErrorMsg(parseResult); + } + // We are only using the prop and areaId option. + VehiclePropValue value = std::move(parseResult.value()); + int32_t propId = value.prop; + int32_t areaId = value.areaId; + VehiclePropValuePool::RecyclableType savedValue; + + { + std::scoped_lock lockGuard(mLock); + auto it = mSavedProps.find(PropIdAreaId{ + .propId = propId, + .areaId = areaId, + }); + if (it == mSavedProps.end()) { + return StringPrintf("No saved property for property: %" PRId32 ", areaID: %" PRId32, + propId, areaId); + } + + savedValue = std::move(it->second); + // Remove the saved property after restoring it. + mSavedProps.erase(it); + } + + // Update timestamp. + savedValue->timestamp = elapsedRealtimeNano(); + + auto writeResult = mServerSidePropStore->writeValue(std::move(savedValue)); + if (!writeResult.ok()) { + return StringPrintf("Failed to restore property value, error: %s", + getErrorMsg(writeResult).c_str()); + } + + return StringPrintf("Property: %" PRId32 ", areaID: %" PRId32 " restored", propId, areaId); +} + +std::string FakeVehicleHardware::dumpInjectEvent(const std::vector& options) { + if (auto result = checkArgumentsSize(options, 3); !result.ok()) { + return getErrorMsg(result); + } + + auto parseResult = parsePropOptions(options); + if (!parseResult.ok()) { + return getErrorMsg(parseResult); + } + VehiclePropValue prop = std::move(parseResult.value()); + ALOGD("Dump: Injecting event from vehicle bus: %s", prop.toString().c_str()); + + eventFromVehicleBus(prop); + + return StringPrintf("Event for property: %d injected", prop.prop); +} + StatusCode FakeVehicleHardware::checkHealth() { // Always return OK for checkHealth. return StatusCode::OK; diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp index f3feea6b46..ab6bf51000 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp @@ -1421,6 +1421,85 @@ TEST_F(FakeVehicleHardwareTest, testDumpSpecificPropertiesNoArg) { 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(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{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({123.4})); +} + TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) { std::vector options; options.push_back("--invalid"); @@ -1681,10 +1760,10 @@ std::vector GenInvalidOptions() { {"--genfakedata", "--startjson"}, "incorrect argument count"}, {"genfakedata_startjson_invalid_repetition", - {"--genfakedata", "--startjson", "file", "0.1"}, + {"--genfakedata", "--startjson", "--path", "file", "0.1"}, "failed to parse repetition as int: \"0.1\""}, {"genfakedata_startjson_invalid_json_file", - {"--genfakedata", "--startjson", "file", "1"}, + {"--genfakedata", "--startjson", "--path", "file", "1"}, "invalid JSON file"}, {"genfakedata_stopjson_no_args", {"--genfakedata", "--stopjson"}, @@ -1765,7 +1844,7 @@ std::string getTestFilePath(const char* filename) { } TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) { - std::vector options = {"--genfakedata", "--startjson", + std::vector options = {"--genfakedata", "--startjson", "--path", getTestFilePath("prop.json"), "2"}; DumpResult result = getHardware()->dump(options); @@ -1790,8 +1869,36 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) { EXPECT_EQ(10, events[7].value.int32Values[0]); } +TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonByContent) { + std::vector 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 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 options = {"--genfakedata", "--startjson", + std::vector options = {"--genfakedata", "--startjson", "--path", getTestFilePath("blahblah.json"), "2"}; DumpResult result = getHardware()->dump(options); @@ -1802,7 +1909,7 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonInvalidFile) { TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStop) { // No iteration number provided, would loop indefinitely. - std::vector options = {"--genfakedata", "--startjson", + std::vector options = {"--genfakedata", "--startjson", "--path", getTestFilePath("prop.json")}; DumpResult result = getHardware()->dump(options); @@ -1810,7 +1917,9 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStop) { ASSERT_FALSE(result.callerShouldDumpState); ASSERT_THAT(result.buffer, HasSubstr("successfully")); - result = getHardware()->dump({"--genfakedata", "--stopjson", getTestFilePath("prop.json")}); + 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")); @@ -1818,7 +1927,7 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStop) { TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStopInvalidFile) { // No iteration number provided, would loop indefinitely. - std::vector options = {"--genfakedata", "--startjson", + std::vector options = {"--genfakedata", "--startjson", "--path", getTestFilePath("prop.json")}; DumpResult result = getHardware()->dump(options); @@ -1826,7 +1935,7 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStopInvalidFile) { ASSERT_FALSE(result.callerShouldDumpState); ASSERT_THAT(result.buffer, HasSubstr("successfully")); - result = getHardware()->dump({"--genfakedata", "--stopjson", getTestFilePath("prop1.json")}); + result = getHardware()->dump({"--genfakedata", "--stopjson", "1234"}); ASSERT_FALSE(result.callerShouldDumpState); ASSERT_THAT(result.buffer, HasSubstr("No JSON event generator found"));