Merge "Support more types for set property cmd."

This commit is contained in:
TreeHugger Robot 2021-06-09 03:23:53 +00:00 committed by Android (Google) Code Review
commit dddbe1565c
4 changed files with 422 additions and 116 deletions

View file

@ -182,6 +182,7 @@ cc_test {
],
shared_libs: [
"libbase",
"libcutils",
],
header_libs: ["libbase_headers"],
test_suites: ["general-tests"],

View file

@ -76,6 +76,9 @@ public:
Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
private:
// Set unit test class as friend class to test private functions.
friend class VehicleHalManagerTestHelper;
using VehiclePropValuePtr = VehicleHal::VehiclePropValuePtr;
// Returns true if needs to call again shortly.
using RetriableAction = std::function<bool()>;
@ -105,14 +108,20 @@ public:
void cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId);
void cmdDumpOneProperty(int fd, int rowNumber, const VehiclePropConfig& config);
bool cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options);
static bool checkArgumentsSize(int fd, const hidl_vec<hidl_string>& options, size_t minSize);
static bool checkCallerHasWritePermissions(int fd);
static bool safelyParseInt(int fd, int index, std::string s, int* out);
template <typename T>
static bool safelyParseInt(int fd, int index, const std::string& s, T* out);
static bool safelyParseFloat(int fd, int index, const std::string& s, float* out);
// Parses "s" as a hex string and populate "*bytes". The hex string must be in the format of
// valid hex format, e.g. "0xABCD".
static bool parseHexString(int fd, const std::string& s, std::vector<uint8_t>* bytes);
void cmdHelp(int fd) const;
void cmdListAllProperties(int fd) const;
void cmdDumpAllProperties(int fd);
void cmdDumpSpecificProperties(int fd, const hidl_vec<hidl_string>& options);
void cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options);
static bool isSubscribable(const VehiclePropConfig& config,
SubscribeFlags flags);
@ -120,7 +129,18 @@ public:
static float checkSampleRate(const VehiclePropConfig& config,
float sampleRate);
static ClientId getClientId(const sp<IVehicleCallback>& callback);
private:
// Parses the cmdline options for "--set" command. "*prop" would be populated with the
// the properties to be set. Returns true when the cmdline options are valid, false otherwise.
static bool parseSetPropOptions(int fd, const hidl_vec<hidl_string>& options,
VehiclePropValue* prop);
// Parses the options and get the values for the current option specified by "*index". "*index"
// would advance to the next option field (e.g., the next "-f"). Returns a list of values for
// the current option.
static std::vector<std::string> getOptionValues(const hidl_vec<hidl_string>& options,
size_t* index);
private:
VehicleHal* mHal;
std::unique_ptr<VehiclePropConfigIndex> mConfigIndex;
SubscriptionManager mSubscriptionManager;

View file

@ -20,7 +20,9 @@
#include <cmath>
#include <fstream>
#include <unordered_set>
#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android/hardware/automotive/vehicle/2.0/BpHwVehicleCallback.h>
@ -44,15 +46,34 @@ using ::android::base::EqualsIgnoreCase;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
namespace {
constexpr std::chrono::milliseconds kHalEventBatchingTimeWindow(10);
const VehiclePropValue kEmptyValue{};
// A list of supported options for "--set" command.
const std::unordered_set<std::string> kSetPropOptions = {
// integer.
"-i",
// 64bit integer.
"-i64",
// float.
"-f",
// string.
"-s",
// bytes in hex format, e.g. 0xDEADBEEF.
"-b",
// Area id in integer.
"-a"};
} // namespace
/**
* Indicates what's the maximum size of hidl_vec<VehiclePropValue> we want
* to store in reusable object pool.
*/
constexpr auto kMaxHidlVecOfVehiclPropValuePoolSize = 20;
constexpr auto kMaxHidlVecOfVehiclePropValuePoolSize = 20;
Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {
ALOGI("getAllPropConfigs called");
@ -213,6 +234,11 @@ void VehicleHalManager::cmdDump(int fd, const hidl_vec<hidl_string>& options) {
} else if (EqualsIgnoreCase(option, "--get")) {
cmdDumpSpecificProperties(fd, options);
} else if (EqualsIgnoreCase(option, "--set")) {
if (!checkCallerHasWritePermissions(fd)) {
dprintf(fd, "Caller does not have write permission\n");
return;
}
// Ignore the return value for this.
cmdSetOneProperty(fd, options);
} else {
dprintf(fd, "Invalid option: %s\n", option.c_str());
@ -239,7 +265,8 @@ bool VehicleHalManager::checkArgumentsSize(int fd, const hidl_vec<hidl_string>&
return false;
}
bool VehicleHalManager::safelyParseInt(int fd, int index, std::string s, int* out) {
template <typename T>
bool VehicleHalManager::safelyParseInt(int fd, int index, const std::string& s, T* out) {
if (!android::base::ParseInt(s, out)) {
dprintf(fd, "non-integer argument at index %d: %s\n", index, s.c_str());
return false;
@ -247,19 +274,27 @@ bool VehicleHalManager::safelyParseInt(int fd, int index, std::string s, int* ou
return true;
}
bool VehicleHalManager::safelyParseFloat(int fd, int index, const std::string& s, float* out) {
if (!android::base::ParseFloat(s, out)) {
dprintf(fd, "non-float argument at index %d: %s\n", index, s.c_str());
return false;
}
return true;
}
void VehicleHalManager::cmdHelp(int fd) const {
dprintf(fd, "Usage: \n\n");
dprintf(fd, "[no args]: dumps (id and value) all supported properties \n");
dprintf(fd, "--help: shows this help\n");
dprintf(fd, "--list: lists the ids of all supported properties\n");
dprintf(fd, "--get <PROP1> [PROP2] [PROPN]: dumps the value of specific properties \n");
// TODO: support other formats (int64, float, bytes)
dprintf(fd,
"--set <PROP> <i|s> <VALUE_1> [<i|s> <VALUE_N>] [a AREA_ID] : sets the value of "
"property PROP, using arbitrary number of key/value parameters (i for int32, "
"s for string) and an optional area.\n"
"Notice that the string value can be set just once, while the other can have multiple "
"values (so they're used in the respective array)\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. "
"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");
}
void VehicleHalManager::cmdListAllProperties(int fd) const {
@ -337,102 +372,49 @@ void VehicleHalManager::cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId)
VehiclePropValue input;
input.prop = prop;
input.areaId = areaId;
auto callback = [&](StatusCode status, const VehiclePropValue& output) {
auto callback = [&fd, &prop](StatusCode status, const VehiclePropValue& output) {
if (status == StatusCode::OK) {
dprintf(fd, "%s\n", toString(output).c_str());
} else {
dprintf(fd, "Could not get property %d. Error: %s\n", prop, toString(status).c_str());
}
};
get(input, callback);
StatusCode status;
auto value = mHal->get(input, &status);
callback(status, value.get() ? *value : kEmptyValue);
}
void VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options) {
if (!checkCallerHasWritePermissions(fd) || !checkArgumentsSize(fd, options, 3)) return;
size_t size = options.size();
// Syntax is --set PROP Type1 Value1 TypeN ValueN, so number of arguments must be even
if (size % 2 != 0) {
dprintf(fd, "must pass even number of arguments (passed %zu)\n", size);
return;
bool VehicleHalManager::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options) {
if (!checkArgumentsSize(fd, options, 4)) {
dprintf(fd, "Requires at least 4 options, see help\n");
return false;
}
int numberValues = (size - 2) / 2;
VehiclePropValue prop;
if (!safelyParseInt(fd, 1, options[1], &prop.prop)) return;
prop.timestamp = elapsedRealtimeNano();
prop.status = VehiclePropertyStatus::AVAILABLE;
// First pass: calculate sizes
int sizeInt32 = 0;
int stringIndex = 0;
int areaIndex = 0;
for (int i = 2, kv = 1; kv <= numberValues; kv++) {
// iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
std::string type = options[i];
std::string value = options[i + 1];
if (EqualsIgnoreCase(type, "i")) {
sizeInt32++;
} else if (EqualsIgnoreCase(type, "s")) {
if (stringIndex != 0) {
dprintf(fd,
"defining string value (%s) again at index %d (already defined at %d=%s"
")\n",
value.c_str(), i, stringIndex, options[stringIndex + 1].c_str());
return;
}
stringIndex = i;
} else if (EqualsIgnoreCase(type, "a")) {
if (areaIndex != 0) {
dprintf(fd,
"defining area value (%s) again at index %d (already defined at %d=%s"
")\n",
value.c_str(), i, areaIndex, options[areaIndex + 1].c_str());
return;
}
areaIndex = i;
} else {
dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i);
return;
}
i += 2;
}
prop.value.int32Values.resize(sizeInt32);
// Second pass: populate it
int indexInt32 = 0;
for (int i = 2, kv = 1; kv <= numberValues; kv++) {
// iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
int valueIndex = i + 1;
std::string type = options[i];
std::string value = options[valueIndex];
if (EqualsIgnoreCase(type, "i")) {
int safeInt;
if (!safelyParseInt(fd, valueIndex, value, &safeInt)) return;
prop.value.int32Values[indexInt32++] = safeInt;
} else if (EqualsIgnoreCase(type, "s")) {
prop.value.stringValue = value;
} else if (EqualsIgnoreCase(type, "a")) {
if (!safelyParseInt(fd, valueIndex, value, &prop.areaId)) return;
}
i += 2;
VehiclePropValue prop = {};
if (!parseSetPropOptions(fd, options, &prop)) {
return false;
}
ALOGD("Setting prop %s", toString(prop).c_str());
auto status = set(prop);
// Do not use VehicleHalManager::set here because we don't want to check write permission.
// Caller should be able to use the debug interface to set read-only properties.
handlePropertySetEvent(prop);
auto status = mHal->set(prop);
if (status == StatusCode::OK) {
dprintf(fd, "Set property %s\n", toString(prop).c_str());
} else {
dprintf(fd, "Failed to set property %s: %s\n", toString(prop).c_str(),
toString(status).c_str());
return true;
}
dprintf(fd, "Failed to set property %s: %s\n", toString(prop).c_str(),
toString(status).c_str());
return false;
}
void VehicleHalManager::init() {
ALOGI("VehicleHalManager::init");
mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclPropValuePoolSize);
mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclePropValuePoolSize);
mBatchingConsumer.run(&mEventQueue,
kHalEventBatchingTimeWindow,
@ -486,7 +468,7 @@ void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>&
for (const HalClientValues& cv : clientValues) {
auto vecSize = cv.values.size();
hidl_vec<VehiclePropValue> vec;
if (vecSize < kMaxHidlVecOfVehiclPropValuePoolSize) {
if (vecSize < kMaxHidlVecOfVehiclePropValuePoolSize) {
vec.setToExternal(&mHidlVecOfVehiclePropValuePool[0], vecSize);
} else {
vec.resize(vecSize);
@ -595,6 +577,158 @@ ClientId VehicleHalManager::getClientId(const sp<IVehicleCallback>& callback) {
}
}
std::vector<std::string> VehicleHalManager::getOptionValues(const hidl_vec<hidl_string>& options,
size_t* index) {
std::vector<std::string> values;
while (*index < options.size()) {
std::string option = options[*index];
if (kSetPropOptions.find(option) != kSetPropOptions.end()) {
return std::move(values);
}
values.push_back(option);
(*index)++;
}
return std::move(values);
}
bool VehicleHalManager::parseSetPropOptions(int fd, const hidl_vec<hidl_string>& options,
VehiclePropValue* prop) {
// Options format:
// --set PROP [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a]
size_t optionIndex = 1;
int propValue;
if (!safelyParseInt(fd, optionIndex, options[optionIndex], &propValue)) {
dprintf(fd, "property value: \"%s\" is not a valid int\n", options[optionIndex].c_str());
return false;
}
prop->prop = propValue;
prop->timestamp = elapsedRealtimeNano();
prop->status = VehiclePropertyStatus::AVAILABLE;
optionIndex++;
std::unordered_set<std::string> parsedOptions;
while (optionIndex < options.size()) {
std::string type = options[optionIndex];
optionIndex++;
size_t currentIndex = optionIndex;
std::vector<std::string> values = getOptionValues(options, &optionIndex);
if (parsedOptions.find(type) != parsedOptions.end()) {
dprintf(fd, "duplicate \"%s\" options\n", type.c_str());
return false;
}
parsedOptions.insert(type);
if (EqualsIgnoreCase(type, "-i")) {
if (values.size() == 0) {
dprintf(fd, "no values specified when using \"-i\"\n");
return false;
}
prop->value.int32Values.resize(values.size());
for (size_t i = 0; i < values.size(); i++) {
int32_t safeInt;
if (!safelyParseInt(fd, currentIndex + i, values[i], &safeInt)) {
dprintf(fd, "value: \"%s\" is not a valid int\n", values[i].c_str());
return false;
}
prop->value.int32Values[i] = safeInt;
}
} else if (EqualsIgnoreCase(type, "-i64")) {
if (values.size() == 0) {
dprintf(fd, "no values specified when using \"-i64\"\n");
return false;
}
prop->value.int64Values.resize(values.size());
for (size_t i = 0; i < values.size(); i++) {
int64_t safeInt;
if (!safelyParseInt(fd, currentIndex + i, values[i], &safeInt)) {
dprintf(fd, "value: \"%s\" is not a valid int64\n", values[i].c_str());
return false;
}
prop->value.int64Values[i] = safeInt;
}
} else if (EqualsIgnoreCase(type, "-f")) {
if (values.size() == 0) {
dprintf(fd, "no values specified when using \"-f\"\n");
return false;
}
prop->value.floatValues.resize(values.size());
for (size_t i = 0; i < values.size(); i++) {
float safeFloat;
if (!safelyParseFloat(fd, currentIndex + i, values[i], &safeFloat)) {
dprintf(fd, "value: \"%s\" is not a valid float\n", values[i].c_str());
return false;
}
prop->value.floatValues[i] = safeFloat;
}
} else if (EqualsIgnoreCase(type, "-s")) {
if (values.size() != 1) {
dprintf(fd, "expect exact one value when using \"-s\"\n");
return false;
}
prop->value.stringValue = values[0];
} else if (EqualsIgnoreCase(type, "-b")) {
if (values.size() != 1) {
dprintf(fd, "expect exact one value when using \"-b\"\n");
return false;
}
std::vector<uint8_t> bytes;
if (!parseHexString(fd, values[0], &bytes)) {
dprintf(fd, "value: \"%s\" is not a valid hex string\n", values[0].c_str());
return false;
}
prop->value.bytes = bytes;
} else if (EqualsIgnoreCase(type, "-a")) {
if (values.size() != 1) {
dprintf(fd, "expect exact one value when using \"-a\"\n");
return false;
}
if (!safelyParseInt(fd, currentIndex, values[0], &(prop->areaId))) {
dprintf(fd, "area ID: \"%s\" is not a valid int\n", values[0].c_str());
return false;
}
} else {
dprintf(fd, "unknown option: %s\n", type.c_str());
return false;
}
}
return true;
}
bool VehicleHalManager::parseHexString(int fd, const std::string& s, std::vector<uint8_t>* bytes) {
if (s.size() % 2 != 0) {
dprintf(fd, "invalid hex string: %s, should have even size\n", s.c_str());
return false;
}
if (strncmp(s.substr(0, 2).c_str(), "0x", 2)) {
dprintf(fd, "hex string should start with \"0x\", got %s\n", s.c_str());
return false;
}
std::string subs = s.substr(2);
std::transform(subs.begin(), subs.end(), subs.begin(),
[](unsigned char c) { return std::tolower(c); });
bool highDigit = true;
for (size_t i = 0; i < subs.size(); i++) {
char c = subs[i];
uint8_t v;
if (c >= '0' && c <= '9') {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a' + 10;
} else {
dprintf(fd, "invalid character %c in hex string %s\n", c, subs.c_str());
return false;
}
if (highDigit) {
(*bytes).push_back(v * 16);
} else {
(*bytes)[bytes->size() - 1] += v;
}
highDigit = !highDigit;
}
return true;
}
} // namespace V2_0
} // namespace vehicle
} // namespace automotive

View file

@ -18,6 +18,7 @@
#include <iostream>
#include <android-base/macros.h>
#include <cutils/native_handle.h>
#include <utils/SystemClock.h>
#include <gtest/gtest.h>
@ -32,6 +33,18 @@ namespace automotive {
namespace vehicle {
namespace V2_0 {
// A simple helper class to expose 'cmdSetOneProperty' to the unit tests.
class VehicleHalManagerTestHelper {
public:
VehicleHalManagerTestHelper(VehicleHalManager* manager) { mManager = manager; }
bool cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options) {
return mManager->cmdSetOneProperty(fd, options);
}
public:
VehicleHalManager* mManager;
};
namespace {
using namespace std::placeholders;
@ -57,33 +70,21 @@ public:
auto property = static_cast<VehicleProperty>(requestedPropValue.prop);
int32_t areaId = requestedPropValue.areaId;
switch (property) {
case VehicleProperty::INFO_MAKE:
pValue = getValuePool()->obtainString(kCarMake);
break;
case VehicleProperty::INFO_FUEL_CAPACITY:
if (fuelCapacityAttemptsLeft-- > 0) {
// Emulate property not ready yet.
*outStatus = StatusCode::TRY_AGAIN;
} else {
pValue = getValuePool()->obtainFloat(42.42);
}
break;
default:
if (requestedPropValue.prop == kCustomComplexProperty) {
pValue = getValuePool()->obtainComplex();
pValue->value.int32Values = hidl_vec<int32_t> { 10, 20 };
pValue->value.int64Values = hidl_vec<int64_t> { 30, 40 };
pValue->value.floatValues = hidl_vec<float_t> { 1.1, 2.2 };
pValue->value.bytes = hidl_vec<uint8_t> { 1, 2, 3 };
pValue->value.stringValue = kCarMake;
break;
}
auto key = makeKey(toInt(property), areaId);
if (mValues.count(key) == 0) {
ALOGW("");
}
pValue = getValuePool()->obtain(mValues[key]);
if (property == VehicleProperty::INFO_FUEL_CAPACITY) {
if (fuelCapacityAttemptsLeft-- > 0) {
// Emulate property not ready yet.
*outStatus = StatusCode::TRY_AGAIN;
} else {
pValue = getValuePool()->obtainFloat(42.42);
}
} else {
auto key = makeKey(requestedPropValue);
if (mValues.count(key) == 0) {
ALOGW("key not found\n");
*outStatus = StatusCode::INVALID_ARG;
return pValue;
}
pValue = getValuePool()->obtain(mValues[key]);
}
if (*outStatus == StatusCode::OK && pValue.get() != nullptr) {
@ -100,7 +101,6 @@ public:
&& mirrorFoldAttemptsLeft-- > 0) {
return StatusCode::TRY_AGAIN;
}
mValues[makeKey(propValue)] = propValue;
return StatusCode::OK;
}
@ -181,6 +181,18 @@ public:
actualStatusCode = refStatus;
}
MockedVehicleHal::VehiclePropValuePtr getComplexProperty() {
auto pValue = objectPool->obtainComplex();
pValue->prop = kCustomComplexProperty;
pValue->areaId = 0;
pValue->value.int32Values = hidl_vec<int32_t>{10, 20};
pValue->value.int64Values = hidl_vec<int64_t>{30, 40};
pValue->value.floatValues = hidl_vec<float_t>{1.1, 2.2};
pValue->value.bytes = hidl_vec<uint8_t>{1, 2, 3};
pValue->value.stringValue = kCarMake;
return pValue;
}
public:
VehiclePropValue actualValue;
StatusCode actualStatusCode;
@ -308,6 +320,8 @@ TEST_F(VehicleHalManagerTest, subscribe_WriteOnly) {
}
TEST_F(VehicleHalManagerTest, get_Complex) {
ASSERT_EQ(StatusCode::OK, hal->set(*getComplexProperty().get()));
invokeGet(kCustomComplexProperty, 0);
ASSERT_EQ(StatusCode::OK, actualStatusCode);
@ -334,6 +348,11 @@ TEST_F(VehicleHalManagerTest, get_Complex) {
}
TEST_F(VehicleHalManagerTest, get_StaticString) {
auto pValue = objectPool->obtainString(kCarMake);
pValue->prop = toInt(VehicleProperty::INFO_MAKE);
pValue->areaId = 0;
ASSERT_EQ(StatusCode::OK, hal->set(*pValue.get()));
invokeGet(toInt(VehicleProperty::INFO_MAKE), 0);
ASSERT_EQ(StatusCode::OK, actualStatusCode);
@ -458,6 +477,138 @@ TEST(HalClientVectorTest, basic) {
ASSERT_TRUE(clients.isEmpty());
}
TEST_F(VehicleHalManagerTest, debug) {
hidl_handle fd = {};
fd.setTo(native_handle_create(/*numFds=*/1, /*numInts=*/0), /*shouldOwn=*/true);
// Because debug function returns void, so no way to check return value.
manager->debug(fd, {});
manager->debug(fd, {"--help"});
manager->debug(fd, {"--list"});
manager->debug(fd, {"--get"});
manager->debug(fd, {"--set"});
manager->debug(fd, {"invalid"});
}
struct SetPropTestCase {
std::string test_name;
const hidl_vec<hidl_string> configs;
bool success;
};
class VehicleHalManagerSetPropTest : public VehicleHalManagerTest,
public testing::WithParamInterface<SetPropTestCase> {};
TEST_P(VehicleHalManagerSetPropTest, cmdSetOneProperty) {
const SetPropTestCase& tc = GetParam();
VehicleHalManagerTestHelper helper(manager.get());
ASSERT_EQ(tc.success, helper.cmdSetOneProperty(STDERR_FILENO, tc.configs));
}
std::vector<SetPropTestCase> GenSetPropParams() {
char infoMakeProperty[100] = {};
snprintf(infoMakeProperty, sizeof(infoMakeProperty), "%d", toInt(VehicleProperty::INFO_MAKE));
return {
{"success_set_string", {"--set", infoMakeProperty, "-s", kCarMake}, true},
{"success_set_bytes", {"--set", infoMakeProperty, "-b", "0xdeadbeef"}, true},
{"success_set_bytes_caps", {"--set", infoMakeProperty, "-b", "0xDEADBEEF"}, true},
{"success_set_int", {"--set", infoMakeProperty, "-i", "2147483647"}, true},
{"success_set_ints",
{"--set", infoMakeProperty, "-i", "2147483647", "0", "-2147483648"},
true},
{"success_set_int64",
{"--set", infoMakeProperty, "-i64", "-9223372036854775808"},
true},
{"success_set_int64s",
{"--set", infoMakeProperty, "-i64", "-9223372036854775808", "0",
"9223372036854775807"},
true},
{"success_set_float", {"--set", infoMakeProperty, "-f", "1.175494351E-38"}, true},
{"success_set_floats",
{"--set", infoMakeProperty, "-f", "-3.402823466E+38", "0", "3.402823466E+38"},
true},
{"success_set_area", {"--set", infoMakeProperty, "-a", "2147483647"}, true},
{"fail_no_options", {}, false},
{"fail_less_than_4_options", {"--set", infoMakeProperty, "-i"}, false},
{"fail_unknown_options", {"--set", infoMakeProperty, "-s", kCarMake, "-abcd"}, false},
{"fail_invalid_property", {"--set", "not valid", "-s", kCarMake}, false},
{"fail_duplicate_string",
{"--set", infoMakeProperty, "-s", kCarMake, "-s", kCarMake},
false},
{"fail_multiple_strings", {"--set", infoMakeProperty, "-s", kCarMake, kCarMake}, false},
{"fail_no_string_value", {"--set", infoMakeProperty, "-s", "-a", "1234"}, false},
{"fail_duplicate_bytes",
{"--set", infoMakeProperty, "-b", "0xdeadbeef", "-b", "0xdeadbeef"},
false},
{"fail_multiple_bytes",
{"--set", infoMakeProperty, "-b", "0xdeadbeef", "0xdeadbeef"},
false},
{"fail_invalid_bytes", {"--set", infoMakeProperty, "-b", "0xgood"}, false},
{"fail_invalid_bytes_no_prefix", {"--set", infoMakeProperty, "-b", "deadbeef"}, false},
{"fail_invalid_int", {"--set", infoMakeProperty, "-i", "abc"}, false},
{"fail_int_out_of_range", {"--set", infoMakeProperty, "-i", "2147483648"}, false},
{"fail_no_int_value", {"--set", infoMakeProperty, "-i", "-s", kCarMake}, false},
{"fail_invalid_int64", {"--set", infoMakeProperty, "-i64", "abc"}, false},
{"fail_int64_out_of_range",
{"--set", infoMakeProperty, "-i64", "-9223372036854775809"},
false},
{"fail_no_int64_value", {"--set", infoMakeProperty, "-i64", "-s", kCarMake}, false},
{"fail_invalid_float", {"--set", infoMakeProperty, "-f", "abc"}, false},
{"fail_float_out_of_range",
{"--set", infoMakeProperty, "-f", "-3.402823466E+39"},
false},
{"fail_no_float_value", {"--set", infoMakeProperty, "-f", "-s", kCarMake}, false},
{"fail_multiple_areas", {"--set", infoMakeProperty, "-a", "2147483648", "0"}, false},
{"fail_invalid_area", {"--set", infoMakeProperty, "-a", "abc"}, false},
{"fail_area_out_of_range", {"--set", infoMakeProperty, "-a", "2147483648"}, false},
{"fail_no_area_value", {"--set", infoMakeProperty, "-a", "-s", kCarMake}, false},
};
}
INSTANTIATE_TEST_SUITE_P(
VehicleHalManagerSetPropTests, VehicleHalManagerSetPropTest,
testing::ValuesIn(GenSetPropParams()),
[](const testing::TestParamInfo<VehicleHalManagerSetPropTest::ParamType>& info) {
return info.param.test_name;
});
TEST_F(VehicleHalManagerTest, SetComplexPropTest) {
char infoMakeProperty[100] = {};
snprintf(infoMakeProperty, sizeof(infoMakeProperty), "%d", toInt(VehicleProperty::INFO_MAKE));
VehicleHalManagerTestHelper helper(manager.get());
ASSERT_TRUE(helper.cmdSetOneProperty(
STDERR_FILENO, {"--set", infoMakeProperty, "-s", kCarMake,
"-b", "0xdeadbeef", "-i", "2147483647",
"0", "-2147483648", "-i64", "-9223372036854775808",
"0", "9223372036854775807", "-f", "-3.402823466E+38",
"0", "3.402823466E+38", "-a", "123"}));
StatusCode status = StatusCode::OK;
VehiclePropValue requestProp;
requestProp.prop = toInt(VehicleProperty::INFO_MAKE);
requestProp.areaId = 123;
auto value = hal->get(requestProp, &status);
ASSERT_EQ(StatusCode::OK, status);
ASSERT_EQ(value->prop, toInt(VehicleProperty::INFO_MAKE));
ASSERT_EQ(value->areaId, 123);
ASSERT_STREQ(kCarMake, value->value.stringValue.c_str());
uint8_t bytes[] = {0xde, 0xad, 0xbe, 0xef};
ASSERT_FALSE(memcmp(bytes, value->value.bytes.data(), sizeof(bytes)));
ASSERT_EQ(3u, value->value.int32Values.size());
ASSERT_EQ(2147483647, value->value.int32Values[0]);
ASSERT_EQ(0, value->value.int32Values[1]);
ASSERT_EQ(-2147483648, value->value.int32Values[2]);
ASSERT_EQ(3u, value->value.int64Values.size());
// -9223372036854775808 is not a valid literal since '-' and '9223372036854775808' would be two
// tokens and the later does not fit in unsigned long long.
ASSERT_EQ(-9223372036854775807 - 1, value->value.int64Values[0]);
ASSERT_EQ(0, value->value.int64Values[1]);
ASSERT_EQ(9223372036854775807, value->value.int64Values[2]);
ASSERT_EQ(3u, value->value.floatValues.size());
ASSERT_EQ(-3.402823466E+38f, value->value.floatValues[0]);
ASSERT_EQ(0.0f, value->value.floatValues[1]);
ASSERT_EQ(3.402823466E+38f, value->value.floatValues[2]);
}
} // namespace anonymous
} // namespace V2_0