Merge changes I467b3aff,Id7abcf28

* changes:
  Add more debug options to FakeVehicleHardware.
  Support fake data generator in FakeVehicleHardware.
This commit is contained in:
Yu Shan
2022-04-27 20:22:16 +00:00
committed by Android (Google) Code Review
9 changed files with 840 additions and 62 deletions

View File

@@ -54,8 +54,8 @@ class GeneratorHub {
void registerGenerator(int32_t generatorId, std::unique_ptr<FakeValueGenerator> 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 {

View File

@@ -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<aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
getAllEvents();
// Whether there are events left to replay for this generator.
bool hasNext();
private:
size_t mEventIndex = 0;
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue> mEvents;
@@ -60,7 +66,8 @@ class JsonFakeValueGenerator : public FakeValueGenerator {
int32_t mNumOfIterations = 0;
void setBit(std::vector<uint8_t>& 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

View File

@@ -58,13 +58,15 @@ void GeneratorHub::registerGenerator(int32_t id, std::unique_ptr<FakeValueGenera
mCond.notify_one();
}
void GeneratorHub::unregisterGenerator(int32_t id) {
bool GeneratorHub::unregisterGenerator(int32_t id) {
bool removed;
{
std::scoped_lock<std::mutex> 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() {

View File

@@ -173,12 +173,11 @@ std::vector<VehiclePropValue> 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<VehiclePropValue> JsonFakeValueGenerator::nextEvent() {
mNumOfIterations--;
}
}
generatedValue.timestamp = mLastEventTimestamp;
return generatedValue;
}
bool JsonFakeValueGenerator::hasNext() {
return mNumOfIterations != 0 && mEvents.size() > 0;
}
} // namespace fake
} // namespace vehicle
} // namespace automotive

View File

@@ -21,10 +21,12 @@
#include <DefaultConfig.h>
#include <FakeObd2Frame.h>
#include <FakeUserHal.h>
#include <GeneratorHub.h>
#include <IVehicleHardware.h>
#include <RecurrentTimer.h>
#include <VehicleHalTypes.h>
#include <VehiclePropertyStore.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleHwKeyInputAction.h>
#include <android-base/parseint.h>
#include <android-base/result.h>
#include <android-base/stringprintf.h>
@@ -132,11 +134,15 @@ class FakeVehicleHardware : public IVehicleHardware {
const std::unique_ptr<FakeUserHal> mFakeUserHal;
// RecurrentTimer is thread-safe.
std::unique_ptr<RecurrentTimer> mRecurrentTimer;
// GeneratorHub is thread-safe.
std::unique_ptr<GeneratorHub> mGeneratorHub;
std::mutex mLock;
std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mLock);
std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash>
mRecurrentActions GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, VehiclePropValuePool::RecyclableType, PropIdAreaIdHash>
mSavedProps GUARDED_BY(mLock);
// PendingRequestHandler is thread-safe.
mutable PendingRequestHandler<GetValuesCallback,
aidl::android::hardware::automotive::vehicle::GetValueRequest>
@@ -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<void> 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<std::string>& options);
std::string dumpSetProperties(const std::vector<std::string>& options);
std::string dumpGetPropertyWithArg(const std::vector<std::string>& options);
std::string dumpSaveProperty(const std::vector<std::string>& options);
std::string dumpRestoreProperty(const std::vector<std::string>& options);
std::string dumpInjectEvent(const std::vector<std::string>& options);
template <typename T>
android::base::Result<T> safelyParseInt(int index, const std::string& s) {
@@ -198,7 +212,7 @@ class FakeVehicleHardware : public IVehicleHardware {
std::vector<std::string> getOptionValues(const std::vector<std::string>& options,
size_t* index);
android::base::Result<aidl::android::hardware::automotive::vehicle::VehiclePropValue>
parseSetPropOptions(const std::vector<std::string>& options);
parsePropOptions(const std::vector<std::string>& options);
android::base::Result<std::vector<uint8_t>> parseHexString(const std::string& s);
android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& 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<std::string>& 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

View File

@@ -22,6 +22,7 @@
#include <DefaultConfig.h>
#include <FakeObd2Frame.h>
#include <JsonFakeValueGenerator.h>
#include <LinearFakeValueGenerator.h>
#include <PropertyUtils.h>
#include <TestPropertyUtils.h>
#include <VehicleHalTypes.h>
@@ -33,6 +34,7 @@
#include <utils/SystemClock.h>
#include <dirent.h>
#include <inttypes.h>
#include <sys/types.h>
#include <fstream>
#include <regex>
@@ -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<std::string> 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<VehiclePropValuePool> 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<VehiclePropValuePool> v
FakeVehicleHardware::~FakeVehicleHardware() {
mPendingGetValueRequests.stop();
mPendingSetValueRequests.stop();
mGeneratorHub.reset();
}
void FakeVehicleHardware::init() {
@@ -575,34 +583,237 @@ DumpResult FakeVehicleHardware::dump(const std::vector<std::string>& 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<std::string>& 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], &currentValue)) {
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<LinearFakeValueGenerator>(
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<JsonFakeValueGenerator> generator;
if (options[2] == "--path") {
const std::string& fileName = options[3];
generator = std::make_unique<JsonFakeValueGenerator>(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<JsonFakeValueGenerator>(/*unused=*/true, content, repetition);
if (!generator->hasNext()) {
return "invalid JSON content, no events";
}
}
int32_t cookie = std::hash<std::string>()(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 <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n"
"--set <PROP> [-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 <PROP1> [PROP2] [PROPN]: dumps the value of specific properties. \n"
"--getWithArg <PROP> [ValueArguments]: gets the value for a specific property with "
"arguments. \n"
"--set <PROP> [ValueArguments]: sets the value of property PROP. \n"
"--save-prop <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 <prop> [-a AREA_ID]: restores a previously saved property value. \n"
"--inject-event <PROP> [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<std::string> FakeVehicleHardware::getOptionValues(
return std::move(values);
}
Result<VehiclePropValue> FakeVehicleHardware::parseSetPropOptions(
Result<VehiclePropValue> FakeVehicleHardware::parsePropOptions(
const std::vector<std::string>& 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<int32_t>(optionIndex, options[optionIndex]);
if (!result.ok()) {
@@ -737,83 +949,98 @@ Result<VehiclePropValue> FakeVehicleHardware::parseSetPropOptions(
std::unordered_set<std::string> parsedOptions;
while (optionIndex < options.size()) {
std::string type = options[optionIndex];
std::string argType = options[optionIndex];
optionIndex++;
size_t currentIndex = optionIndex;
std::vector<std::string> values = getOptionValues(options, &optionIndex);
if (parsedOptions.find(type) != parsedOptions.end()) {
return Error() << StringPrintf("Duplicate \"%s\" options\n", type.c_str());
std::vector<std::string> 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<int32_t>(currentIndex + i, values[i]);
prop.value.int32Values.resize(argValuesSize);
for (size_t i = 0; i < argValuesSize; i++) {
auto int32Result = safelyParseInt<int32_t>(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<int64_t>(currentIndex + i, values[i]);
prop.value.int64Values.resize(argValuesSize);
for (size_t i = 0; i < argValuesSize; i++) {
auto int64Result = safelyParseInt<int64_t>(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<int32_t>(currentIndex, values[0]);
auto int32Result = safelyParseInt<int32_t>(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<int64_t>(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<std::string
return getErrorMsg(result);
}
auto parseResult = parseSetPropOptions(options);
auto parseResult = parsePropOptions(options);
if (!parseResult.ok()) {
return getErrorMsg(parseResult);
}
@@ -848,6 +1075,123 @@ std::string FakeVehicleHardware::dumpSetProperties(const std::vector<std::string
getErrorMsg(setResult).c_str());
}
std::string FakeVehicleHardware::dumpGetPropertyWithArg(const std::vector<std::string>& 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<std::string>& 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<std::mutex> 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<std::string>& 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<std::mutex> 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<std::string>& 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;

View File

@@ -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"],
}

View File

@@ -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<FakeVehicleHardware>();
auto callback = std::make_unique<IVehicleHardware::PropertyChangeCallback>(
[this](const std::vector<VehiclePropValue>& values) {
onPropertyChangeEvent(values);
@@ -98,7 +101,13 @@ class FakeVehicleHardwareTest : public ::testing::Test {
[this](std::vector<GetValueResult> 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<SetValueRequest>& requests) {
{
@@ -251,6 +260,14 @@ class FakeVehicleHardwareTest : public ::testing::Test {
return mChangedProperties;
}
bool waitForChangedProperties(size_t count, milliseconds timeout) {
std::unique_lock<std::mutex> lk(mLock);
return mCv.wait_for(lk, timeout, [this, count] {
ScopedLockAssertion lockAssertion(mLock);
return mChangedProperties.size() >= count;
});
}
bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count,
milliseconds timeout) {
PropIdAreaId propIdAreaId{
@@ -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<std::mutex> lockGuard(mLock);
return mEventCount[propIdAreaId];
}
static void addSetValueRequest(std::vector<SetValueRequest>& requests,
std::vector<SetValueResult>& expectedResults, int64_t requestId,
const VehiclePropValue& value, StatusCode expectedStatus) {
@@ -332,7 +358,7 @@ class FakeVehicleHardwareTest : public ::testing::Test {
} mPropValueCmp;
private:
FakeVehicleHardware mHardware;
std::unique_ptr<FakeVehicleHardware> mHardware;
std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback;
std::shared_ptr<IVehicleHardware::GetValuesCallback> 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<size_t>(3))
<< "expect 3 obd2 freeze frames stored";
std::string propIdStr = StringPrintf("%d", OBD2_FREEZE_FRAME);
DumpResult result;
for (int64_t timestamp : propValue.value.int64Values) {
result = getHardware()->dump(
{"--getWithArg", propIdStr, "-i64", StringPrintf("%" PRId64, timestamp)});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_NE(result.buffer, "");
ASSERT_THAT(result.buffer, ContainsRegex("Get property result:"));
}
// Set the timestamp argument to 0.
result = getHardware()->dump({"--getWithArg", propIdStr, "-i64", "0"});
ASSERT_FALSE(result.callerShouldDumpState);
// There is no freeze obd2 frame at timestamp 0.
ASSERT_THAT(result.buffer, ContainsRegex("failed to read property value"));
}
TEST_F(FakeVehicleHardwareTest, testSaveRestoreProp) {
int32_t prop = toInt(VehicleProperty::TIRE_PRESSURE);
std::string propIdStr = std::to_string(prop);
std::string areaIdStr = std::to_string(WHEEL_FRONT_LEFT);
DumpResult result = getHardware()->dump({"--save-prop", propIdStr, "-a", areaIdStr});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, ContainsRegex("saved"));
ASSERT_EQ(setValue(VehiclePropValue{
.prop = prop,
.areaId = WHEEL_FRONT_LEFT,
.value =
{
.floatValues = {210.0},
},
}),
StatusCode::OK);
result = getHardware()->dump({"--restore-prop", propIdStr, "-a", areaIdStr});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, ContainsRegex("restored"));
auto getResult = getValue(VehiclePropValue{.prop = prop, .areaId = WHEEL_FRONT_LEFT});
ASSERT_TRUE(getResult.ok());
// The default value is 200.0.
ASSERT_EQ(getResult.value().value.floatValues, std::vector<float>{200.0});
}
TEST_F(FakeVehicleHardwareTest, testDumpInjectEvent) {
int32_t prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED);
std::string propIdStr = std::to_string(prop);
int64_t timestamp = elapsedRealtimeNano();
// Inject an event with float value 123.4 and timestamp.
DumpResult result = getHardware()->dump(
{"--inject-event", propIdStr, "-f", "123.4", "-t", std::to_string(timestamp)});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer,
ContainsRegex(StringPrintf("Event for property: %d injected", prop)));
ASSERT_TRUE(waitForChangedProperties(prop, 0, /*count=*/1, milliseconds(1000)))
<< "No changed event received for injected event from vehicle bus";
auto events = getChangedProperties();
ASSERT_EQ(events.size(), 1u);
auto event = events[0];
ASSERT_EQ(event.timestamp, timestamp);
ASSERT_EQ(event.value.floatValues, std::vector<float>({123.4}));
}
TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
std::vector<std::string> options;
options.push_back("--invalid");
@@ -1606,6 +1711,260 @@ TEST_F(FakeVehicleHardwareTest, SetComplexPropTest) {
ASSERT_EQ(3.402823466E+38f, value.value.floatValues[2]);
}
struct OptionsTestCase {
std::string name;
std::vector<std::string> options;
std::string expectMsg;
};
class FakeVehicleHardwareOptionsTest : public FakeVehicleHardwareTest,
public testing::WithParamInterface<OptionsTestCase> {};
std::vector<OptionsTestCase> GenInvalidOptions() {
return {{"unknown_command", {"--unknown"}, "Invalid option: --unknown"},
{"help", {"--help"}, "Usage:"},
{"genfakedata_no_subcommand",
{"--genfakedata"},
"No subcommand specified for genfakedata"},
{"genfakedata_unknown_subcommand",
{"--genfakedata", "--unknown"},
"Unknown command: \"--unknown\""},
{"genfakedata_start_linear_no_args",
{"--genfakedata", "--startlinear"},
"incorrect argument count"},
{"genfakedata_start_linear_invalid_propId",
{"--genfakedata", "--startlinear", "abcd", "0.1", "0.1", "0.1", "0.1", "100000000"},
"failed to parse propId as int: \"abcd\""},
{"genfakedata_start_linear_invalid_middleValue",
{"--genfakedata", "--startlinear", "1", "abcd", "0.1", "0.1", "0.1", "100000000"},
"failed to parse middleValue as float: \"abcd\""},
{"genfakedata_start_linear_invalid_currentValue",
{"--genfakedata", "--startlinear", "1", "0.1", "abcd", "0.1", "0.1", "100000000"},
"failed to parse currentValue as float: \"abcd\""},
{"genfakedata_start_linear_invalid_dispersion",
{"--genfakedata", "--startlinear", "1", "0.1", "0.1", "abcd", "0.1", "100000000"},
"failed to parse dispersion as float: \"abcd\""},
{"genfakedata_start_linear_invalid_increment",
{"--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "abcd", "100000000"},
"failed to parse increment as float: \"abcd\""},
{"genfakedata_start_linear_invalid_interval",
{"--genfakedata", "--startlinear", "1", "0.1", "0.1", "0.1", "0.1", "0.1"},
"failed to parse interval as int: \"0.1\""},
{"genfakedata_stop_linear_no_args",
{"--genfakedata", "--stoplinear"},
"incorrect argument count"},
{"genfakedata_stop_linear_invalid_propId",
{"--genfakedata", "--stoplinear", "abcd"},
"failed to parse propId as int: \"abcd\""},
{"genfakedata_startjson_no_args",
{"--genfakedata", "--startjson"},
"incorrect argument count"},
{"genfakedata_startjson_invalid_repetition",
{"--genfakedata", "--startjson", "--path", "file", "0.1"},
"failed to parse repetition as int: \"0.1\""},
{"genfakedata_startjson_invalid_json_file",
{"--genfakedata", "--startjson", "--path", "file", "1"},
"invalid JSON file"},
{"genfakedata_stopjson_no_args",
{"--genfakedata", "--stopjson"},
"incorrect argument count"},
{"genfakedata_keypress_no_args",
{"--genfakedata", "--keypress"},
"incorrect argument count"},
{"genfakedata_keypress_invalid_keyCode",
{"--genfakedata", "--keypress", "0.1", "1"},
"failed to parse keyCode as int: \"0.1\""},
{"genfakedata_keypress_invalid_display",
{"--genfakedata", "--keypress", "1", "0.1"},
"failed to parse display as int: \"0.1\""}};
}
TEST_P(FakeVehicleHardwareOptionsTest, testInvalidOptions) {
auto tc = GetParam();
DumpResult result = getHardware()->dump(tc.options);
EXPECT_FALSE(result.callerShouldDumpState);
EXPECT_THAT(result.buffer, HasSubstr(tc.expectMsg));
}
INSTANTIATE_TEST_SUITE_P(
FakeVehicleHardwareOptionsTests, FakeVehicleHardwareOptionsTest,
testing::ValuesIn(GenInvalidOptions()),
[](const testing::TestParamInfo<FakeVehicleHardwareOptionsTest::ParamType>& info) {
return info.param.name;
});
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) {
// Start a fake linear data generator for vehicle speed at 0.1s interval.
// range: 0 - 100, current value: 30, step: 20.
std::string propIdString = StringPrintf("%d", toInt(VehicleProperty::PERF_VEHICLE_SPEED));
std::vector<std::string> options = {"--genfakedata", "--startlinear", propIdString,
/*middleValue=*/"50",
/*currentValue=*/"30",
/*dispersion=*/"50",
/*increment=*/"20",
/*interval=*/"100000000"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
ASSERT_TRUE(waitForChangedProperties(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0, /*count=*/5,
milliseconds(1000)))
<< "not enough events generated for linear data generator";
int32_t value = 30;
auto events = getChangedProperties();
for (size_t i = 0; i < 5; i++) {
ASSERT_EQ(1u, events[i].value.floatValues.size());
EXPECT_EQ(static_cast<float>(value), events[i].value.floatValues[0]);
value = (value + 20) % 100;
}
// Stop the linear generator.
options = {"--genfakedata", "--stoplinear", propIdString};
result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
clearChangedProperties();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// There should be no new events generated.
EXPECT_EQ(0u, getEventCount(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0));
}
std::string getTestFilePath(const char* filename) {
static std::string baseDir = android::base::GetExecutableDirectory();
return baseDir + "/" + filename;
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) {
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("prop.json"), "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
ASSERT_TRUE(waitForChangedProperties(/*count=*/8, milliseconds(1000)))
<< "not enough events generated for JSON data generator";
auto events = getChangedProperties();
ASSERT_EQ(8u, events.size());
// First set of events, we test 1st and the last.
EXPECT_EQ(1u, events[0].value.int32Values.size());
EXPECT_EQ(8, events[0].value.int32Values[0]);
EXPECT_EQ(1u, events[3].value.int32Values.size());
EXPECT_EQ(10, events[3].value.int32Values[0]);
// Second set of the same events.
EXPECT_EQ(1u, events[4].value.int32Values.size());
EXPECT_EQ(8, events[4].value.int32Values[0]);
EXPECT_EQ(1u, events[7].value.int32Values.size());
EXPECT_EQ(10, events[7].value.int32Values[0]);
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonByContent) {
std::vector<std::string> options = {
"--genfakedata", "--startjson", "--content",
"[{\"timestamp\":1000000,\"areaId\":0,\"value\":8,\"prop\":289408000}]", "1"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
ASSERT_TRUE(waitForChangedProperties(/*count=*/1, milliseconds(1000)))
<< "not enough events generated for JSON data generator";
auto events = getChangedProperties();
ASSERT_EQ(1u, events.size());
EXPECT_EQ(1u, events[0].value.int32Values.size());
EXPECT_EQ(8, events[0].value.int32Values[0]);
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonInvalidContent) {
std::vector<std::string> options = {"--genfakedata", "--startjson", "--content", "[{", "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("invalid JSON content"));
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonInvalidFile) {
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("blahblah.json"), "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("invalid JSON file"));
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStop) {
// No iteration number provided, would loop indefinitely.
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("prop.json")};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
std::string id = result.buffer.substr(result.buffer.find("ID: ") + 4);
result = getHardware()->dump({"--genfakedata", "--stopjson", id});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStopInvalidFile) {
// No iteration number provided, would loop indefinitely.
std::vector<std::string> options = {"--genfakedata", "--startjson", "--path",
getTestFilePath("prop.json")};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
result = getHardware()->dump({"--genfakedata", "--stopjson", "1234"});
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("No JSON event generator found"));
// TearDown function should destroy the generator which stops the iteration.
}
TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) {
std::vector<std::string> options = {"--genfakedata", "--keypress", "1", "2"};
DumpResult result = getHardware()->dump(options);
ASSERT_FALSE(result.callerShouldDumpState);
ASSERT_THAT(result.buffer, HasSubstr("successfully"));
auto events = getChangedProperties();
ASSERT_EQ(2u, events.size());
EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[0].prop);
EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[1].prop);
ASSERT_EQ(3u, events[0].value.int32Values.size());
ASSERT_EQ(3u, events[1].value.int32Values.size());
EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_DOWN), events[0].value.int32Values[0]);
EXPECT_EQ(1, events[0].value.int32Values[1]);
EXPECT_EQ(2, events[0].value.int32Values[2]);
EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_UP), events[1].value.int32Values[0]);
EXPECT_EQ(1, events[1].value.int32Values[1]);
EXPECT_EQ(2, events[1].value.int32Values[2]);
}
TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) {
ASSERT_EQ(setValue(VehiclePropValue{
.prop = ECHO_REVERSE_BYTES,

View File

@@ -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
}
]