[url] add methods to parse parameters from url (#9392)

This commit adds methods to parse parameter values directly from the
url, moves the url unit test to the test/unit, and updates the url
processing methods in posix platform.
This commit is contained in:
Zhanglong Xia
2023-09-14 08:46:11 -07:00
committed by GitHub
parent 31df6363ab
commit 20a12eb728
9 changed files with 605 additions and 261 deletions
-12
View File
@@ -32,15 +32,3 @@ add_library(openthread-url EXCLUDE_FROM_ALL
target_link_libraries(openthread-url PRIVATE ot-config)
if(BUILD_TESTING)
add_executable(ot-test-url
url.cpp
)
target_compile_definitions(ot-test-url
PRIVATE -DSELF_TEST=1
)
target_link_libraries(ot-test-url
PRIVATE ot-config
)
add_test(NAME ot-test-url COMMAND ot-test-url)
endif()
+84 -111
View File
@@ -110,116 +110,89 @@ exit:
return rval;
}
otError Url::ParseUint32(const char *aName, uint32_t &aValue) const
{
otError error = OT_ERROR_NONE;
const char *str;
long long value;
VerifyOrExit((str = GetValue(aName)) != nullptr, error = OT_ERROR_NOT_FOUND);
value = strtoll(str, nullptr, 0);
VerifyOrExit(0 <= value && value <= UINT32_MAX, error = OT_ERROR_INVALID_ARGS);
aValue = static_cast<uint32_t>(value);
exit:
return error;
}
otError Url::ParseUint16(const char *aName, uint16_t &aValue) const
{
otError error = OT_ERROR_NONE;
uint32_t value;
SuccessOrExit(error = ParseUint32(aName, value));
VerifyOrExit(value <= UINT16_MAX, error = OT_ERROR_INVALID_ARGS);
aValue = static_cast<uint16_t>(value);
exit:
return error;
}
otError Url::ParseUint8(const char *aName, uint8_t &aValue) const
{
otError error = OT_ERROR_NONE;
uint32_t value;
SuccessOrExit(error = ParseUint32(aName, value));
VerifyOrExit(value <= UINT8_MAX, error = OT_ERROR_INVALID_ARGS);
aValue = static_cast<uint8_t>(value);
exit:
return error;
}
otError Url::ParseInt32(const char *aName, int32_t &aValue) const
{
otError error = OT_ERROR_NONE;
const char *str;
long long value;
VerifyOrExit((str = GetValue(aName)) != nullptr, error = OT_ERROR_NOT_FOUND);
value = strtoll(str, nullptr, 0);
VerifyOrExit(INT32_MIN <= value && value <= INT32_MAX, error = OT_ERROR_INVALID_ARGS);
aValue = static_cast<int32_t>(value);
exit:
return error;
}
otError Url::ParseInt16(const char *aName, int16_t &aValue) const
{
otError error = OT_ERROR_NONE;
int32_t value;
SuccessOrExit(error = ParseInt32(aName, value));
VerifyOrExit(INT16_MIN <= value && value <= INT16_MAX, error = OT_ERROR_INVALID_ARGS);
aValue = static_cast<int16_t>(value);
exit:
return error;
}
otError Url::ParseInt8(const char *aName, int8_t &aValue) const
{
otError error = OT_ERROR_NONE;
int32_t value;
SuccessOrExit(error = ParseInt32(aName, value));
VerifyOrExit(INT8_MIN <= value && value <= INT8_MAX, error = OT_ERROR_INVALID_ARGS);
aValue = static_cast<int8_t>(value);
exit:
return error;
}
} // namespace Url
} // namespace ot
#ifndef SELF_TEST
#define SELF_TEST 0
#endif
#if SELF_TEST
#include <assert.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
void TestSimple(void)
{
char url[] = "spinel:///dev/ttyUSB0?baudrate=115200";
ot::Url::Url args;
assert(!args.Init(url));
assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
assert(!strcmp(args.GetValue("baudrate"), "115200"));
assert(args.GetValue("not-exists") == nullptr);
assert(args.GetValue("last-value-wrong-position", url) == nullptr);
assert(args.GetValue("last-value-before-url", url - 1) == nullptr);
assert(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr);
printf("PASS %s\r\n", __func__);
}
void TestSimpleNoQueryString(void)
{
char url[] = "spinel:///dev/ttyUSB0";
ot::Url::Url args;
assert(!args.Init(url));
assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
assert(args.GetValue("last-value-wrong-position", url) == nullptr);
assert(args.GetValue("last-value-before-url", url - 1) == nullptr);
assert(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr);
printf("PASS %s\r\n", __func__);
}
void TestEmptyValue(void)
{
char url[] = "spinel:///dev/ttyUSB0?rtscts&baudrate=115200&verbose&verbose&verbose";
ot::Url::Url args;
const char *arg = nullptr;
assert(!args.Init(url));
assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
assert((arg = args.GetValue("rtscts")) != nullptr);
assert(args.GetValue("rtscts", arg) == nullptr);
assert((arg = args.GetValue("verbose", arg)) != nullptr);
assert((arg = args.GetValue("verbose", arg)) != nullptr);
assert((arg = args.GetValue("verbose", arg)) != nullptr);
assert((arg = args.GetValue("verbose", arg)) == nullptr);
printf("PASS %s\r\n", __func__);
}
void TestMultipleProtocols(void)
{
char url[] = "spinel+spi:///dev/ttyUSB0?baudrate=115200";
ot::Url::Url args;
assert(!args.Init(url));
assert(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
assert(!strcmp(args.GetValue("baudrate"), "115200"));
printf("PASS %s\r\n", __func__);
}
void TestMultipleProtocolsAndDuplicateParameters(void)
{
char url[] = "spinel+exec:///path/to/ot-rcp?arg=1&arg=arg2&arg=3";
ot::Url::Url args;
const char *arg = nullptr;
assert(!args.Init(url));
assert(!strcmp(args.GetPath(), "/path/to/ot-rcp"));
arg = args.GetValue("arg");
assert(!strcmp(arg, "1"));
arg = args.GetValue("arg", arg);
assert(!strcmp(arg, "arg2"));
arg = args.GetValue("arg", arg);
assert(!strcmp(arg, "3"));
assert(args.GetValue("arg", url) == nullptr);
assert(args.GetValue("arg", url - 1) == nullptr);
assert(args.GetValue("arg", url + sizeof(url)) == nullptr);
printf("PASS %s\r\n", __func__);
}
#pragma GCC diagnostic pop
int main(void)
{
TestSimple();
TestSimpleNoQueryString();
TestEmptyValue();
TestMultipleProtocols();
TestMultipleProtocolsAndDuplicateParameters();
return 0;
}
#endif // SELF_TEST
+111
View File
@@ -29,6 +29,7 @@
#ifndef OT_LIB_URL_URL_HPP_
#define OT_LIB_URL_URL_HPP_
#include <stdint.h>
#include <openthread/error.h>
/**
@@ -92,6 +93,116 @@ public:
*
*/
const char *GetProtocol(void) const { return mProtocol; }
/**
* Indicates whether or not the url contains the parameter.
*
* @param[in] aName A pointer to the parameter name.
*
* @retval TRUE The url contains the parameter.
* @retval FALSE The url doesn't support the parameter.
*
*/
bool HasParam(const char *aName) const { return (GetValue(aName) != nullptr); }
/**
* Parses a `uint32_t` parameter value.
*
* The parameter value in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix).
*
* @param[in] aName A pointer to the parameter name.
* @param[out] aValue A reference to an `uint32_t` variable to output the parameter value.
* The original value of @p aValue won't change if failed to get the value.
*
* @retval OT_ERROR_NONE The parameter value was parsed successfully.
* @retval OT_ERROR_NOT_FOUND The parameter name was not found.
* @retval OT_ERROR_INVALID_ARGS The parameter value was not contain valid number (e.g., value out of range).
*
*/
otError ParseUint32(const char *aName, uint32_t &aValue) const;
/**
* Parses a `uint16_t` parameter value.
*
* The parameter value in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix).
*
* @param[in] aName A pointer to the parameter name.
* @param[out] aValue A reference to an `uint16_t` variable to output the parameter value.
* The original value of @p aValue won't change if failed to get the value.
*
* @retval OT_ERROR_NONE The parameter value was parsed successfully.
* @retval OT_ERROR_NOT_FOUND The parameter name was not found.
* @retval OT_ERROR_INVALID_ARGS The parameter value was not contain valid number (e.g., value out of range).
*
*/
otError ParseUint16(const char *aName, uint16_t &aValue) const;
/**
* Parses a `uint8_t` parameter value.
*
* The parameter value in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix).
*
* @param[in] aName A pointer to the parameter name.
* @param[out] aValue A reference to an `uint16_t` variable to output the parameter value.
* The original value of @p aValue won't change if failed to get the value.
*
* @retval OT_ERROR_NONE The parameter value was parsed successfully.
* @retval OT_ERROR_NOT_FOUND The parameter name was not found.
* @retval OT_ERROR_INVALID_ARGS The parameter value was not contain valid number (e.g., value out of range).
*
*/
otError ParseUint8(const char *aName, uint8_t &aValue) const;
/**
* Parses a `int32_t` parameter value.
*
* The parameter value in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix). The string
* can start with `+`/`-` sign.
*
* @param[in] aName A pointer to the parameter name.
* @param[out] aValue A reference to an `int32_t` variable to output the parameter value.
* The original value of @p aValue won't change if failed to get the value.
*
* @retval OT_ERROR_NONE The parameter value was parsed successfully.
* @retval OT_ERROR_NOT_FOUND The parameter name was not found.
* @retval OT_ERROR_INVALID_ARGS The parameter value was not contain valid number (e.g., value out of range).
*
*/
otError ParseInt32(const char *aName, int32_t &aValue) const;
/**
* Parses a `int16_t` parameter value.
*
* The parameter value in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix). The string
* can start with `+`/`-` sign.
*
* @param[in] aName A pointer to the parameter name.
* @param[out] aValue A reference to an `int16_t` variable to output the parameter value.
* The original value of @p aValue won't change if failed to get the value.
*
* @retval OT_ERROR_NONE The parameter value was parsed successfully.
* @retval OT_ERROR_NOT_FOUND The parameter name was not found.
* @retval OT_ERROR_INVALID_ARGS The parameter value was not contain valid number (e.g., value out of range).
*
*/
otError ParseInt16(const char *aName, int16_t &aValue) const;
/**
* Parses a `int8_t` parameter value.
*
* The parameter value in string is parsed as decimal or hex format (if contains `0x` or `0X` prefix). The string
* can start with `+`/`-` sign.
*
* @param[in] aName A pointer to the parameter name.
* @param[out] aValue A reference to an `int8_t` variable to output the parameter value.
* The original value of @p aValue won't change if failed to get the value.
*
* @retval OT_ERROR_NONE The parameter value was parsed successfully.
* @retval OT_ERROR_NOT_FOUND The parameter name was not found.
* @retval OT_ERROR_INVALID_ARGS The parameter value was not contain valid number (e.g., value out of range).
*
*/
otError ParseInt8(const char *aName, int8_t &aValue) const;
};
} // namespace Url
+6 -13
View File
@@ -452,9 +452,8 @@ int HdlcInterface::OpenFile(const Url::Url &aRadioUrl)
struct termios tios;
const char *value;
speed_t speed;
int stopBit = 1;
uint32_t baudrate = 115200;
uint8_t stopBit = 1;
uint32_t baudrate = 115200;
VerifyOrExit((rval = tcgetattr(fd, &tios)) == 0);
@@ -479,10 +478,7 @@ int HdlcInterface::OpenFile(const Url::Url &aRadioUrl)
}
}
if ((value = aRadioUrl.GetValue("uart-stop")) != nullptr)
{
stopBit = atoi(value);
}
IgnoreError(aRadioUrl.ParseUint8("uart-stop", stopBit));
switch (stopBit)
{
@@ -497,10 +493,7 @@ int HdlcInterface::OpenFile(const Url::Url &aRadioUrl)
break;
}
if ((value = aRadioUrl.GetValue("uart-baudrate")))
{
baudrate = static_cast<uint32_t>(atoi(value));
}
IgnoreError(aRadioUrl.ParseUint32("uart-baudrate", baudrate));
switch (baudrate)
{
@@ -591,7 +584,7 @@ int HdlcInterface::OpenFile(const Url::Url &aRadioUrl)
mBaudRate = baudrate;
if (aRadioUrl.GetValue("uart-flow-control") != nullptr)
if (aRadioUrl.HasParam("uart-flow-control"))
{
tios.c_cflag |= CRTSCTS;
}
@@ -718,7 +711,7 @@ otError HdlcInterface::ResetConnection(void)
otError error = OT_ERROR_NONE;
uint64_t end;
if (mRadioUrl->GetValue("uart-reset") != nullptr)
if (mRadioUrl->HasParam("uart-reset"))
{
usleep(static_cast<useconds_t>(kRemoveRcpDelay) * US_PER_MS);
CloseFile();
+87 -82
View File
@@ -87,57 +87,57 @@ Radio::Radio(const char *aUrl)
void Radio::Init(void)
{
bool resetRadio = (mRadioUrl.GetValue("no-reset") == nullptr);
bool restoreDataset = (mRadioUrl.GetValue("ncp-dataset") != nullptr);
bool skipCompatibilityCheck = (mRadioUrl.GetValue("skip-rcp-compatibility-check") != nullptr);
const char *parameterValue;
const char *region;
#if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
const char *maxPowerTable;
#endif
bool resetRadio = !mRadioUrl.HasParam("no-reset");
bool skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check");
#if OPENTHREAD_POSIX_VIRTUAL_TIME
// The last argument must be the node id
{
const char *nodeId = nullptr;
for (const char *arg = nullptr; (arg = mRadioUrl.GetValue("forkpty-arg", arg)) != nullptr; nodeId = arg)
{
}
virtualTimeInit(static_cast<uint16_t>(atoi(nodeId)));
}
VirtualTimeInit();
#endif
if (restoreDataset)
SuccessOrDie(sRadioSpinel.GetSpinelInterface().Init(mRadioUrl));
sRadioSpinel.Init(resetRadio, skipCompatibilityCheck);
ProcessRadioUrl(mRadioUrl);
}
#if OPENTHREAD_POSIX_VIRTUAL_TIME
void Radio::VirtualTimeInit(void)
{
// The last argument must be the node id
const char *nodeId = nullptr;
for (const char *arg = nullptr; (arg = mRadioUrl.GetValue("forkpty-arg", arg)) != nullptr; nodeId = arg)
{
}
virtualTimeInit(static_cast<uint16_t>(atoi(nodeId)));
}
#endif
void Radio::ProcessRadioUrl(const RadioUrl &aRadioUrl)
{
const char *region;
int8_t value;
if (aRadioUrl.HasParam("ncp-dataset"))
{
otLogCritPlat("The argument \"ncp-dataset\" is no longer supported");
DieNow(OT_ERROR_FAILED);
}
SuccessOrDie(sRadioSpinel.GetSpinelInterface().Init(mRadioUrl));
sRadioSpinel.Init(resetRadio, skipCompatibilityCheck);
parameterValue = mRadioUrl.GetValue("fem-lnagain");
if (parameterValue != nullptr)
if (aRadioUrl.HasParam("fem-lnagain"))
{
long femLnaGain = strtol(parameterValue, nullptr, 0);
VerifyOrDie(INT8_MIN <= femLnaGain && femLnaGain <= INT8_MAX, OT_EXIT_INVALID_ARGUMENTS);
SuccessOrDie(sRadioSpinel.SetFemLnaGain(static_cast<int8_t>(femLnaGain)));
SuccessOrDie(aRadioUrl.ParseInt8("fem-lnagain", value));
SuccessOrDie(sRadioSpinel.SetFemLnaGain(value));
}
parameterValue = mRadioUrl.GetValue("cca-threshold");
if (parameterValue != nullptr)
if (aRadioUrl.HasParam("cca-threshold"))
{
long ccaThreshold = strtol(parameterValue, nullptr, 0);
VerifyOrDie(INT8_MIN <= ccaThreshold && ccaThreshold <= INT8_MAX, OT_EXIT_INVALID_ARGUMENTS);
SuccessOrDie(sRadioSpinel.SetCcaEnergyDetectThreshold(static_cast<int8_t>(ccaThreshold)));
SuccessOrDie(aRadioUrl.ParseInt8("cca-threshold", value));
SuccessOrDie(sRadioSpinel.SetCcaEnergyDetectThreshold(value));
}
region = mRadioUrl.GetValue("region");
if (region != nullptr)
if ((region = aRadioUrl.GetValue("region")) != nullptr)
{
uint16_t regionCode;
@@ -146,55 +146,11 @@ void Radio::Init(void)
SuccessOrDie(otPlatRadioSetRegion(gInstance, regionCode));
}
#if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
maxPowerTable = mRadioUrl.GetValue("max-power-table");
if (maxPowerTable != nullptr)
{
constexpr int8_t kPowerDefault = 30; // Default power 1 watt (30 dBm).
const char *str = nullptr;
uint8_t channel = ot::Radio::kChannelMin;
int8_t power = kPowerDefault;
otError error;
ProcessMaxPowerTable(aRadioUrl);
for (str = strtok(const_cast<char *>(maxPowerTable), ","); str != nullptr && channel <= ot::Radio::kChannelMax;
str = strtok(nullptr, ","))
{
power = static_cast<int8_t>(strtol(str, nullptr, 0));
error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power);
if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_IMPLEMENTED)
{
DieNow(OT_ERROR_FAILED);
}
else if (error == OT_ERROR_NOT_IMPLEMENTED)
{
otLogWarnPlat("The RCP doesn't support setting the max transmit power");
}
++channel;
}
// Use the last power if omitted.
while (channel <= ot::Radio::kChannelMax)
{
error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power);
if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_IMPLEMENTED)
{
DieNow(OT_ERROR_FAILED);
}
else if (error == OT_ERROR_NOT_IMPLEMENTED)
{
otLogWarnPlat("The RCP doesn't support setting the max transmit power");
}
++channel;
}
VerifyOrDie(str == nullptr, OT_EXIT_INVALID_ARGUMENTS);
}
#endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
{
const char *enableCoex = mRadioUrl.GetValue("enable-coex");
const char *enableCoex = aRadioUrl.GetValue("enable-coex");
if (enableCoex != nullptr)
{
SuccessOrDie(sRadioSpinel.SetCoexEnabled(enableCoex[0] != '0'));
@@ -203,6 +159,55 @@ void Radio::Init(void)
#endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
}
void Radio::ProcessMaxPowerTable(const RadioUrl &aRadioUrl)
{
OT_UNUSED_VARIABLE(aRadioUrl);
#if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
otError error;
constexpr int8_t kPowerDefault = 30; // Default power 1 watt (30 dBm).
const char *str = nullptr;
char *pSave = nullptr;
uint8_t channel = ot::Radio::kChannelMin;
int8_t power = kPowerDefault;
const char *maxPowerTable;
VerifyOrExit((maxPowerTable = aRadioUrl.GetValue("max-power-table")) != nullptr);
for (str = strtok_r(const_cast<char *>(maxPowerTable), ",", &pSave);
str != nullptr && channel <= ot::Radio::kChannelMax; str = strtok_r(nullptr, ",", &pSave))
{
power = static_cast<int8_t>(strtol(str, nullptr, 0));
error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power);
VerifyOrDie((error == OT_ERROR_NONE) || (error == OT_ERROR_NOT_IMPLEMENTED), OT_EXIT_FAILURE);
if (error == OT_ERROR_NOT_IMPLEMENTED)
{
otLogWarnPlat("The RCP doesn't support setting the max transmit power");
}
++channel;
}
// Use the last power if omitted.
while (channel <= ot::Radio::kChannelMax)
{
error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power);
VerifyOrDie((error == OT_ERROR_NONE) || (error == OT_ERROR_NOT_IMPLEMENTED), OT_ERROR_FAILED);
if (error == OT_ERROR_NOT_IMPLEMENTED)
{
otLogWarnPlat("The RCP doesn't support setting the max transmit power");
}
++channel;
}
VerifyOrDie(str == nullptr, OT_EXIT_INVALID_ARGUMENTS);
exit:
return;
#endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE
}
void *Radio::GetSpinelInstance(void) { return &sRadioSpinel; }
} // namespace Posix
+6
View File
@@ -64,6 +64,12 @@ public:
static void *GetSpinelInstance(void);
private:
#if OPENTHREAD_POSIX_VIRTUAL_TIME
void VirtualTimeInit(void);
#endif
void ProcessRadioUrl(const RadioUrl &aRadioUrl);
void ProcessMaxPowerTable(const RadioUrl &aRadioUrl);
RadioUrl mRadioUrl;
};
+12 -43
View File
@@ -119,7 +119,6 @@ otError SpiInterface::Init(const Url::Url &aRadioUrl)
uint16_t spiCsDelay = OT_PLATFORM_CONFIG_SPI_DEFAULT_CS_DELAY_US;
uint8_t spiAlignAllowance = OT_PLATFORM_CONFIG_SPI_DEFAULT_ALIGN_ALLOWANCE;
uint8_t spiSmallPacketSize = OT_PLATFORM_CONFIG_SPI_DEFAULT_SMALL_PACKET_SIZE;
const char *value;
spiGpioIntDevice = aRadioUrl.GetValue("gpio-int-device");
spiGpioResetDevice = aRadioUrl.GetValue("gpio-reset-device");
@@ -128,48 +127,18 @@ otError SpiInterface::Init(const Url::Url &aRadioUrl)
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
if ((value = aRadioUrl.GetValue("gpio-int-line")))
{
spiGpioIntLine = static_cast<uint8_t>(atoi(value));
}
else
{
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
if ((value = aRadioUrl.GetValue("gpio-reset-line")))
{
spiGpioResetLine = static_cast<uint8_t>(atoi(value));
}
else
{
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
if ((value = aRadioUrl.GetValue("spi-mode")))
{
spiMode = static_cast<uint8_t>(atoi(value));
}
if ((value = aRadioUrl.GetValue("spi-speed")))
{
spiSpeed = static_cast<uint32_t>(atoi(value));
}
if ((value = aRadioUrl.GetValue("spi-reset-delay")))
{
spiResetDelay = static_cast<uint32_t>(atoi(value));
}
if ((value = aRadioUrl.GetValue("spi-cs-delay")))
{
spiCsDelay = static_cast<uint16_t>(atoi(value));
}
if ((value = aRadioUrl.GetValue("spi-align-allowance")))
{
spiAlignAllowance = static_cast<uint8_t>(atoi(value));
}
if ((value = aRadioUrl.GetValue("spi-small-packet")))
{
spiSmallPacketSize = static_cast<uint8_t>(atoi(value));
}
VerifyOrDie(spiAlignAllowance <= kSpiAlignAllowanceMax, OT_EXIT_FAILURE);
SuccessOrDie(aRadioUrl.ParseUint8("gpio-int-line", spiGpioIntLine));
SuccessOrDie(aRadioUrl.ParseUint8("gpio-reset-line", spiGpioResetLine));
VerifyOrDie(aRadioUrl.ParseUint8("spi-mode", spiMode) != OT_ERROR_INVALID_ARGS, OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie(aRadioUrl.ParseUint32("spi-speed", spiSpeed) != OT_ERROR_INVALID_ARGS, OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie(aRadioUrl.ParseUint32("spi-reset-delay", spiResetDelay) != OT_ERROR_INVALID_ARGS,
OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie(aRadioUrl.ParseUint16("spi-cs-delay", spiCsDelay) != OT_ERROR_INVALID_ARGS, OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie(aRadioUrl.ParseUint8("spi-align-allowance", spiAlignAllowance) != OT_ERROR_INVALID_ARGS,
OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie(aRadioUrl.ParseUint8("spi-small-packet", spiSmallPacketSize) != OT_ERROR_INVALID_ARGS,
OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie(spiAlignAllowance <= kSpiAlignAllowanceMax, OT_EXIT_INVALID_ARGUMENTS);
mSpiResetDelay = spiResetDelay;
mSpiCsDelayUs = spiCsDelay;
+18
View File
@@ -1229,3 +1229,21 @@ target_link_libraries(ot-test-address-sanitizer
${COMMON_LIBS}
)
add_test(NAME ot-test-address-sanitizer COMMAND ot-test-address-sanitizer)
add_executable(ot-test-url
test_url.cpp
)
target_include_directories(ot-test-url
PRIVATE
${COMMON_INCLUDES}
)
target_compile_options(ot-test-url
PRIVATE
${COMMON_COMPILE_OPTIONS}
)
target_link_libraries(ot-test-url
PRIVATE
${COMMON_LIBS}
openthread-url
)
add_test(NAME ot-test-url COMMAND ot-test-url)
+281
View File
@@ -0,0 +1,281 @@
/*
* Copyright (c) 2023, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include "test_util.h"
#include "lib/url/url.hpp"
namespace ot {
namespace Url {
void TestSimple(void)
{
char url[] = "spinel:///dev/ttyUSB0?baudrate=115200";
ot::Url::Url args;
VerifyOrQuit(!args.Init(url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
VerifyOrQuit(!strcmp(args.GetValue("baudrate"), "115200"));
VerifyOrQuit(args.GetValue("not-exists") == nullptr);
VerifyOrQuit(args.GetValue("last-value-wrong-position", url) == nullptr);
VerifyOrQuit(args.GetValue("last-value-before-url", url - 1) == nullptr);
VerifyOrQuit(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr);
printf("PASS %s\r\n", __func__);
}
void TestSimpleNoQueryString(void)
{
char url[] = "spinel:///dev/ttyUSB0";
ot::Url::Url args;
VerifyOrQuit(!args.Init(url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
VerifyOrQuit(args.GetValue("last-value-wrong-position", url) == nullptr);
VerifyOrQuit(args.GetValue("last-value-before-url", url - 1) == nullptr);
VerifyOrQuit(args.GetValue("last-value-after-url", url + sizeof(url)) == nullptr);
printf("PASS %s\r\n", __func__);
}
void TestEmptyValue(void)
{
char url[] = "spinel:///dev/ttyUSB0?rtscts&baudrate=115200&verbose&verbose&verbose";
ot::Url::Url args;
const char *arg = nullptr;
VerifyOrQuit(!args.Init(url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
VerifyOrQuit((arg = args.GetValue("rtscts")) != nullptr);
VerifyOrQuit(args.GetValue("rtscts", arg) == nullptr);
VerifyOrQuit((arg = args.GetValue("verbose", arg)) != nullptr);
VerifyOrQuit((arg = args.GetValue("verbose", arg)) != nullptr);
VerifyOrQuit((arg = args.GetValue("verbose", arg)) != nullptr);
VerifyOrQuit((arg = args.GetValue("verbose", arg)) == nullptr);
printf("PASS %s\r\n", __func__);
}
void TestMultipleProtocols(void)
{
char url[] = "spinel+spi:///dev/ttyUSB0?baudrate=115200";
ot::Url::Url args;
VerifyOrQuit(!args.Init(url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
VerifyOrQuit(!strcmp(args.GetValue("baudrate"), "115200"));
printf("PASS %s\r\n", __func__);
}
void TestMultipleProtocolsAndDuplicateParameters(void)
{
char url[] = "spinel+exec:///path/to/ot-rcp?arg=1&arg=arg2&arg=3";
ot::Url::Url args;
const char *arg = nullptr;
VerifyOrQuit(!args.Init(url));
VerifyOrQuit(!strcmp(args.GetPath(), "/path/to/ot-rcp"));
arg = args.GetValue("arg");
VerifyOrQuit(!strcmp(arg, "1"));
arg = args.GetValue("arg", arg);
VerifyOrQuit(!strcmp(arg, "arg2"));
arg = args.GetValue("arg", arg);
VerifyOrQuit(!strcmp(arg, "3"));
VerifyOrQuit(args.GetValue("arg", url) == nullptr);
VerifyOrQuit(args.GetValue("arg", url - 1) == nullptr);
VerifyOrQuit(args.GetValue("arg", url + sizeof(url)) == nullptr);
printf("PASS %s\r\n", __func__);
}
void TestIntValue(void)
{
char int8url[] = "spinel:///dev/ttyUSB0?no-reset&val1=1&val2=0x02&val3=-0X03&val4=-4&val5=+5&val6=128&val7=-129";
char int16url[] = "spinel:///dev/ttyUSB0?val1=1&val2=0x02&val3=-0X03&val4=-400&val5=+500&val6=32768&val7=-32769";
char int32url[] =
"spinel:///dev/ttyUSB0?val1=1&val2=0x02&val3=-0X03&val4=-40000&val5=+50000&val6=2147483648&val7=-2147483649";
ot::Url::Url args;
int8_t int8val;
int16_t int16val;
int32_t int32val;
VerifyOrQuit(!args.Init(int8url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
VerifyOrQuit(args.HasParam("no-reset"));
VerifyOrQuit(!args.HasParam("reset"));
SuccessOrQuit(args.ParseInt8("val1", int8val));
VerifyOrQuit(int8val == 1);
SuccessOrQuit(args.ParseInt8("val2", int8val));
VerifyOrQuit(int8val == 2);
SuccessOrQuit(args.ParseInt8("val3", int8val));
VerifyOrQuit(int8val == -3);
SuccessOrQuit(args.ParseInt8("val4", int8val));
VerifyOrQuit(int8val == -4);
SuccessOrQuit(args.ParseInt8("val5", int8val));
VerifyOrQuit(int8val == 5);
VerifyOrQuit(args.ParseInt8("val6", int8val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(int8val == 5);
VerifyOrQuit(args.ParseInt8("val7", int8val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(int8val == 5);
VerifyOrQuit(args.ParseInt8("val8", int8val) == OT_ERROR_NOT_FOUND);
VerifyOrQuit(int8val == 5);
VerifyOrQuit(!args.Init(int16url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
SuccessOrQuit(args.ParseInt16("val1", int16val));
VerifyOrQuit(int16val == 1);
SuccessOrQuit(args.ParseInt16("val2", int16val));
VerifyOrQuit(int16val == 2);
SuccessOrQuit(args.ParseInt16("val3", int16val));
VerifyOrQuit(int16val == -3);
SuccessOrQuit(args.ParseInt16("val4", int16val));
VerifyOrQuit(int16val == -400);
SuccessOrQuit(args.ParseInt16("val5", int16val));
VerifyOrQuit(int16val == 500);
VerifyOrQuit(args.ParseInt16("val6", int16val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(int16val == 500);
VerifyOrQuit(args.ParseInt16("val7", int16val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(int16val == 500);
VerifyOrQuit(args.ParseInt16("val8", int16val) == OT_ERROR_NOT_FOUND);
VerifyOrQuit(int16val == 500);
VerifyOrQuit(!args.Init(int32url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
SuccessOrQuit(args.ParseInt32("val1", int32val));
VerifyOrQuit(int32val == 1);
SuccessOrQuit(args.ParseInt32("val2", int32val));
VerifyOrQuit(int32val == 2);
SuccessOrQuit(args.ParseInt32("val3", int32val));
VerifyOrQuit(int32val == -3);
SuccessOrQuit(args.ParseInt32("val4", int32val));
VerifyOrQuit(int32val == -40000);
SuccessOrQuit(args.ParseInt32("val5", int32val));
VerifyOrQuit(int32val == 50000);
VerifyOrQuit(args.ParseInt32("val6", int32val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(int32val == 50000);
VerifyOrQuit(args.ParseInt32("val7", int32val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(int32val == 50000);
VerifyOrQuit(args.ParseInt32("val8", int32val) == OT_ERROR_NOT_FOUND);
VerifyOrQuit(int32val == 50000);
printf("PASS %s\r\n", __func__);
}
void TestUintValue(void)
{
char uint8url[] = "spinel:///dev/ttyUSB0?no-reset&val1=1&val2=0x02&val3=0X03&val4=-4&val5=+5&val6=256&val7=-1";
char uint16url[] = "spinel:///dev/ttyUSB0?val1=1&val2=0x02&val3=0X03&val4=-400&val5=+500&val6=65536&val7=-1";
char uint32url[] =
"spinel:///dev/ttyUSB0?val1=1&val2=0x02&val3=0X03&val4=-40000&val5=+70000&val6=4294967296&val7=-1";
ot::Url::Url args;
uint8_t uint8val;
uint16_t uint16val;
uint32_t uint32val;
VerifyOrQuit(!args.Init(uint8url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
SuccessOrQuit(args.ParseUint8("val1", uint8val));
VerifyOrQuit(uint8val == 1);
SuccessOrQuit(args.ParseUint8("val2", uint8val));
VerifyOrQuit(uint8val == 2);
SuccessOrQuit(args.ParseUint8("val3", uint8val));
VerifyOrQuit(uint8val == 3);
VerifyOrQuit(args.ParseUint8("val4", uint8val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint8val == 3);
SuccessOrQuit(args.ParseUint8("val5", uint8val));
VerifyOrQuit(uint8val == 5);
VerifyOrQuit(args.ParseUint8("val6", uint8val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint8val == 5);
VerifyOrQuit(args.ParseUint8("val7", uint8val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint8val == 5);
VerifyOrQuit(args.ParseUint8("val8", uint8val) == OT_ERROR_NOT_FOUND);
VerifyOrQuit(uint8val == 5);
VerifyOrQuit(!args.Init(uint16url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
SuccessOrQuit(args.ParseUint16("val1", uint16val));
VerifyOrQuit(uint16val == 1);
SuccessOrQuit(args.ParseUint16("val2", uint16val));
VerifyOrQuit(uint16val == 2);
SuccessOrQuit(args.ParseUint16("val3", uint16val));
VerifyOrQuit(uint16val == 3);
VerifyOrQuit(args.ParseUint16("val4", uint16val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint16val == 3);
SuccessOrQuit(args.ParseUint16("val5", uint16val));
VerifyOrQuit(uint16val == 500);
VerifyOrQuit(args.ParseUint16("val6", uint16val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint16val == 500);
VerifyOrQuit(args.ParseUint16("val7", uint16val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint16val == 500);
VerifyOrQuit(args.ParseUint16("val8", uint16val) == OT_ERROR_NOT_FOUND);
VerifyOrQuit(uint16val == 500);
VerifyOrQuit(!args.Init(uint32url));
VerifyOrQuit(!strcmp(args.GetPath(), "/dev/ttyUSB0"));
SuccessOrQuit(args.ParseUint32("val1", uint32val));
VerifyOrQuit(uint32val == 1);
SuccessOrQuit(args.ParseUint32("val2", uint32val));
VerifyOrQuit(uint32val == 2);
SuccessOrQuit(args.ParseUint32("val3", uint32val));
VerifyOrQuit(uint32val == 3);
VerifyOrQuit(args.ParseUint32("val4", uint32val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint32val == 3);
SuccessOrQuit(args.ParseUint32("val5", uint32val));
VerifyOrQuit(uint32val == 70000);
VerifyOrQuit(args.ParseUint32("val6", uint32val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint32val == 70000);
VerifyOrQuit(args.ParseUint32("val7", uint32val) == OT_ERROR_INVALID_ARGS);
VerifyOrQuit(uint32val == 70000);
VerifyOrQuit(args.ParseUint32("val8", uint32val) == OT_ERROR_NOT_FOUND);
VerifyOrQuit(uint32val == 70000);
printf("PASS %s\r\n", __func__);
}
} // namespace Url
} // namespace ot
int main(void)
{
ot::Url::TestSimple();
ot::Url::TestSimpleNoQueryString();
ot::Url::TestEmptyValue();
ot::Url::TestMultipleProtocols();
ot::Url::TestMultipleProtocolsAndDuplicateParameters();
ot::Url::TestIntValue();
ot::Url::TestUintValue();
return 0;
}