mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
Merge changes I467b3aff,Id7abcf28
* changes: Add more debug options to FakeVehicleHardware. Support fake data generator in FakeVehicleHardware.
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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], ¤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<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;
|
||||
|
||||
@@ -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"],
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user