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..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; @@ -53,6 +56,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; @@ -60,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/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..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,12 +244,15 @@ std::optional JsonFakeValueGenerator::nextEvent() { mNumOfIterations--; } } - generatedValue.timestamp = mLastEventTimestamp; 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..8cc19b12f8 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,11 +134,15 @@ 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); std::unordered_map, PropIdAreaIdHash> mRecurrentActions GUARDED_BY(mLock); + std::unordered_map + mSavedProps GUARDED_BY(mLock); // PendingRequestHandler is thread-safe. mutable PendingRequestHandler @@ -156,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, @@ -184,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) { @@ -198,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, @@ -207,6 +221,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..a7e986b9ca 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 @@ -33,6 +34,7 @@ #include #include +#include #include #include #include @@ -55,6 +57,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; @@ -86,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 @@ -140,6 +145,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) { eventFromVehicleBus(value); })), mPendingGetValueRequests(this), mPendingSetValueRequests(this) { init(); @@ -148,6 +155,7 @@ FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr v FakeVehicleHardware::~FakeVehicleHardware() { mPendingGetValueRequests.stop(); mPendingSetValueRequests.stop(); + mGeneratorHub.reset(); } void FakeVehicleHardware::init() { @@ -575,34 +583,237 @@ 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(); } 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 --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, + "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 --startjson --content [jsonContent]: Start a JSON generator using the content. + +--genfakedata --stopjson [generatorID(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 --path path repetition + // or + // --genfakedata --startjson --content content repetition. + if (options.size() != 4 && options.size() != 5) { + return "incorrect argument count, need 4 or 5 arguments for --genfakedata " + "--startjson\n"; + } + // Iterate infinitely if repetition number is not provided + int32_t repetition = -1; + if (options.size() == 5) { + if (!android::base::ParseInt(options[4], &repetition)) { + return parseErrMsg("repetition", options[4], "int"); + } + } + 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 StringPrintf("JSON event generator started successfully, ID: %" PRId32, cookie); + } else if (command == "--stopjson") { + // --genfakedata --stopjson [generatorID(string)] + if (options.size() != 3) { + return "incorrect argument count, need 3 arguments for --genfakedata --stopjson\n"; + } + 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 ID: %s", options[2].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; +} + +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" - "Fake user HAL usage: \n" + - mFakeUserHal->showDumpHelp(); + "BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n" + + genFakeDataHelp() + "Fake user HAL usage: \n" + mFakeUserHal->showDumpHelp(); } std::string FakeVehicleHardware::dumpAllProperties() { @@ -720,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()) { @@ -737,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()); } } @@ -825,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/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..ab6bf51000 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; @@ -1395,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"); @@ -1606,6 +1711,260 @@ 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", "--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\""}}; +} + +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", "--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 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", "--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 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 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 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 + } +]