From 20a12eb7283d17ec704d6eaf8ee436778f2c2526 Mon Sep 17 00:00:00 2001 From: Zhanglong Xia Date: Thu, 14 Sep 2023 23:46:11 +0800 Subject: [PATCH] [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. --- src/lib/url/CMakeLists.txt | 12 -- src/lib/url/url.cpp | 195 ++++++++---------- src/lib/url/url.hpp | 111 ++++++++++ src/posix/platform/hdlc_interface.cpp | 19 +- src/posix/platform/radio.cpp | 169 ++++++++-------- src/posix/platform/radio.hpp | 6 + src/posix/platform/spi_interface.cpp | 55 ++--- tests/unit/CMakeLists.txt | 18 ++ tests/unit/test_url.cpp | 281 ++++++++++++++++++++++++++ 9 files changed, 605 insertions(+), 261 deletions(-) create mode 100644 tests/unit/test_url.cpp diff --git a/src/lib/url/CMakeLists.txt b/src/lib/url/CMakeLists.txt index 0ed677bc1..a4b393a78 100644 --- a/src/lib/url/CMakeLists.txt +++ b/src/lib/url/CMakeLists.txt @@ -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() diff --git a/src/lib/url/url.cpp b/src/lib/url/url.cpp index ed576d257..0d1250ba0 100644 --- a/src/lib/url/url.cpp +++ b/src/lib/url/url.cpp @@ -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(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(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(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(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(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(value); + +exit: + return error; +} + } // namespace Url } // namespace ot - -#ifndef SELF_TEST -#define SELF_TEST 0 -#endif - -#if SELF_TEST -#include - -#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 diff --git a/src/lib/url/url.hpp b/src/lib/url/url.hpp index c336e2ae4..ee6ec1883 100644 --- a/src/lib/url/url.hpp +++ b/src/lib/url/url.hpp @@ -29,6 +29,7 @@ #ifndef OT_LIB_URL_URL_HPP_ #define OT_LIB_URL_URL_HPP_ +#include #include /** @@ -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 diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index b1fb297f8..edc58c494 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -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(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(kRemoveRcpDelay) * US_PER_MS); CloseFile(); diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index c9d613395..c96f803cf 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -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(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(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(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(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(maxPowerTable), ","); str != nullptr && channel <= ot::Radio::kChannelMax; - str = strtok(nullptr, ",")) - { - power = static_cast(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(maxPowerTable), ",", &pSave); + str != nullptr && channel <= ot::Radio::kChannelMax; str = strtok_r(nullptr, ",", &pSave)) + { + power = static_cast(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 diff --git a/src/posix/platform/radio.hpp b/src/posix/platform/radio.hpp index 50ca95d39..5e8d81cf9 100644 --- a/src/posix/platform/radio.hpp +++ b/src/posix/platform/radio.hpp @@ -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; }; diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index c8a7f0ce7..af6ee369e 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -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(atoi(value)); - } - else - { - DieNow(OT_EXIT_INVALID_ARGUMENTS); - } - if ((value = aRadioUrl.GetValue("gpio-reset-line"))) - { - spiGpioResetLine = static_cast(atoi(value)); - } - else - { - DieNow(OT_EXIT_INVALID_ARGUMENTS); - } - if ((value = aRadioUrl.GetValue("spi-mode"))) - { - spiMode = static_cast(atoi(value)); - } - if ((value = aRadioUrl.GetValue("spi-speed"))) - { - spiSpeed = static_cast(atoi(value)); - } - if ((value = aRadioUrl.GetValue("spi-reset-delay"))) - { - spiResetDelay = static_cast(atoi(value)); - } - if ((value = aRadioUrl.GetValue("spi-cs-delay"))) - { - spiCsDelay = static_cast(atoi(value)); - } - if ((value = aRadioUrl.GetValue("spi-align-allowance"))) - { - spiAlignAllowance = static_cast(atoi(value)); - } - if ((value = aRadioUrl.GetValue("spi-small-packet"))) - { - spiSmallPacketSize = static_cast(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; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 19465a96f..5ffdbd33b 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -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) diff --git a/tests/unit/test_url.cpp b/tests/unit/test_url.cpp new file mode 100644 index 000000000..e10796e9e --- /dev/null +++ b/tests/unit/test_url.cpp @@ -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 + +#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; +}