mirror of
https://github.com/espressif/openthread.git
synced 2026-08-07 03:07:47 +00:00
[cmd-parser] adding helper 'ParseAs{Type}' function (#5607)
This commit adds new helper functions under `Utils::CmdLineParser` to
parse a given ASCII string as different simple types, namely signed or
unsigned integers of different bit lengths (`uint8/16/32/64`, and
`int8/16/32`). The new functions validate that the parsed value fits
within the supported range of the expected int type. This commit also
adds different flavors of `ParseAsHexString()` to parse a hex string
into sequence of bytes (with fixed/known size or unknown size allowing
or disallowing truncation). It also moves the parse related methods
(like `ParseAsIp6Prefix`) from CLI modules in `CmdLineParser`.
The CLI modules are then updated to use the new `ParseAs{Type}`
functions simplifying the code and adding extra checks when parsing
the CLI input.
Finally, this commit adds a unit test `test_cmd_line_parser` verifying
the behavior of new `ParseAs{Type}()` functions.
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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_platform.h"
|
||||
|
||||
#include <openthread/config.h>
|
||||
|
||||
#include "common/instance.hpp"
|
||||
#include "utils/parse_cmdline.hpp"
|
||||
|
||||
#include "test_util.h"
|
||||
|
||||
using ot::Utils::CmdLineParser::ParseAsBool;
|
||||
using ot::Utils::CmdLineParser::ParseAsHexString;
|
||||
using ot::Utils::CmdLineParser::ParseAsInt16;
|
||||
using ot::Utils::CmdLineParser::ParseAsInt32;
|
||||
using ot::Utils::CmdLineParser::ParseAsInt8;
|
||||
using ot::Utils::CmdLineParser::ParseAsIp6Address;
|
||||
using ot::Utils::CmdLineParser::ParseAsIp6Prefix;
|
||||
using ot::Utils::CmdLineParser::ParseAsUint16;
|
||||
using ot::Utils::CmdLineParser::ParseAsUint32;
|
||||
using ot::Utils::CmdLineParser::ParseAsUint64;
|
||||
using ot::Utils::CmdLineParser::ParseAsUint8;
|
||||
|
||||
template <typename ValueType> struct TestCase
|
||||
{
|
||||
const char *mString;
|
||||
otError mError;
|
||||
ValueType mValue;
|
||||
};
|
||||
|
||||
template <typename ValueType, otError (&Parser)(const char *aString, ValueType &aValue)>
|
||||
void VerifyParser(const TestCase<ValueType> *aTestCases, const char *aParserName, const char *aPrintFormat)
|
||||
{
|
||||
const TestCase<ValueType> *testCase = aTestCases;
|
||||
ValueType value;
|
||||
otError error;
|
||||
|
||||
printf("----------------------------------------------------------\n");
|
||||
|
||||
while (true)
|
||||
{
|
||||
printf("%s(\"%s\") -> ", aParserName, testCase->mString);
|
||||
|
||||
if (testCase->mError != OT_ERROR_NONE)
|
||||
{
|
||||
printf("error:%s", otThreadErrorToString(testCase->mError));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(aPrintFormat, testCase->mValue);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
error = Parser(testCase->mString, value);
|
||||
|
||||
VerifyOrQuit(error == testCase->mError, "Parser did not return the expected error");
|
||||
|
||||
if (error == OT_ERROR_NONE)
|
||||
{
|
||||
VerifyOrQuit(value == testCase->mValue, "Parser failed");
|
||||
}
|
||||
|
||||
if (testCase->mString[0] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
testCase++;
|
||||
}
|
||||
}
|
||||
|
||||
void TestParsingInts(void)
|
||||
{
|
||||
TestCase<bool> kBoolTestCases[] = {
|
||||
{"0", OT_ERROR_NONE, false}, // Zero as false value
|
||||
{"1", OT_ERROR_NONE, true}, // Non-zero as true value
|
||||
{"0x0", OT_ERROR_NONE, false}, // Zero as false value
|
||||
{"0x1", OT_ERROR_NONE, true}, // Non-zero (in hex) as true value
|
||||
{"10", OT_ERROR_NONE, true}, // Non-zero as true value
|
||||
{"a", OT_ERROR_INVALID_ARGS, false}, // Error case: Incorrect char
|
||||
{"-1", OT_ERROR_INVALID_ARGS, false}, // Error case: Negative value
|
||||
{"", OT_ERROR_INVALID_ARGS, false}, // Empty string indicate end of the list
|
||||
};
|
||||
|
||||
TestCase<uint8_t> kUint8TestCases[] = {
|
||||
{"0", OT_ERROR_NONE, 0},
|
||||
{"1", OT_ERROR_NONE, 1},
|
||||
{"74", OT_ERROR_NONE, 74},
|
||||
{"255", OT_ERROR_NONE, 255}, // Max `uint8` value (decimal format)
|
||||
{"0xa", OT_ERROR_NONE, 0xa},
|
||||
{"0x04", OT_ERROR_NONE, 4},
|
||||
{"0x7e", OT_ERROR_NONE, 0x7e},
|
||||
{"0xcd", OT_ERROR_NONE, 0xcd},
|
||||
{"0x0", OT_ERROR_NONE, 0},
|
||||
{"0xff", OT_ERROR_NONE, 0xff}, // Max `uint8` value (hex format)
|
||||
{"0x0000ff", OT_ERROR_NONE, 0xff}, // Hex format (extra zeros)
|
||||
{"0xB", OT_ERROR_NONE, 0xb},
|
||||
{"0X04", OT_ERROR_NONE, 4},
|
||||
{"0X7E", OT_ERROR_NONE, 0x7e},
|
||||
{"0XCD", OT_ERROR_NONE, 0xcd},
|
||||
{"0X0", OT_ERROR_NONE, 0},
|
||||
{"0XFF", OT_ERROR_NONE, 0xff},
|
||||
{"00", OT_ERROR_NONE, 0},
|
||||
{"-5", OT_ERROR_INVALID_ARGS, 0}, // Error case: Negative value.
|
||||
{"0y", OT_ERROR_INVALID_ARGS, 0}, // Error case: Incorrect prefix.
|
||||
{"0x7g", OT_ERROR_INVALID_ARGS, 0}, // Error case: Bad hex char.
|
||||
{"0xaaa", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out or range.
|
||||
{"256", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
|
||||
{"12e", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra char.
|
||||
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list
|
||||
};
|
||||
|
||||
TestCase<uint16_t> kUint16TestCases[] = {
|
||||
{"0", OT_ERROR_NONE, 0},
|
||||
{"1245", OT_ERROR_NONE, 1245},
|
||||
{"0xa", OT_ERROR_NONE, 0xa},
|
||||
{"0xab7d", OT_ERROR_NONE, 0xab7d},
|
||||
{"0X1AE", OT_ERROR_NONE, 0x1ae},
|
||||
{"0X7E", OT_ERROR_NONE, 0x7e},
|
||||
{"65535", OT_ERROR_NONE, 65535}, // Max `uint16` value (decimal format)
|
||||
{"0xffff", OT_ERROR_NONE, 0xffff}, // Max `uint16` value (hex format)
|
||||
{"-1", OT_ERROR_INVALID_ARGS, 0}, // Error case: Negative value
|
||||
{"0y", OT_ERROR_INVALID_ARGS, 0}, // Error case: Incorrect prefix
|
||||
{"0xq", OT_ERROR_INVALID_ARGS, 0}, // Error case: Bad hex char.
|
||||
{"0x12345", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range.
|
||||
{"65536", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
|
||||
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list
|
||||
};
|
||||
|
||||
TestCase<uint32_t> kUint32TestCases[] = {
|
||||
{"0", OT_ERROR_NONE, 0},
|
||||
{"1234567", OT_ERROR_NONE, 1234567},
|
||||
{"0xc", OT_ERROR_NONE, 0xc},
|
||||
{"0x01234567", OT_ERROR_NONE, 0x1234567},
|
||||
{"0XABCDEF09", OT_ERROR_NONE, 0xabcdef09},
|
||||
{"0X54321", OT_ERROR_NONE, 0x54321},
|
||||
{"4294967295", OT_ERROR_NONE, 4294967295}, // Max `uint32` value (decimal format)
|
||||
{"0xffffffff", OT_ERROR_NONE, 0xffffffff}, // Max `uint32` value (hex format)
|
||||
{"-1", OT_ERROR_INVALID_ARGS, 0},
|
||||
{"0y", OT_ERROR_INVALID_ARGS, 0},
|
||||
{"0x1234zz", OT_ERROR_INVALID_ARGS, 0}, // Error case: Bad hex char
|
||||
{"0x123456789", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range
|
||||
{"4294967296", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
|
||||
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
|
||||
};
|
||||
|
||||
TestCase<uint64_t> kUint64TestCases[] = {
|
||||
{"0", OT_ERROR_NONE, 0},
|
||||
{"123456789087654321", OT_ERROR_NONE, 123456789087654321},
|
||||
{"0xb", OT_ERROR_NONE, 0xb},
|
||||
{"0x1234567890acbdef", OT_ERROR_NONE, 0x1234567890acbdef},
|
||||
{"0XFEDCBA9876543210", OT_ERROR_NONE, 0xfedcba9876543210},
|
||||
{"0xffffffffffffffff", OT_ERROR_NONE, 0xffffffffffffffff}, // Max `uint64` value (hex format)
|
||||
{"18446744073709551615", OT_ERROR_NONE, 18446744073709551615ull}, // Max `uint64` value (decimal format)
|
||||
{"-1", OT_ERROR_INVALID_ARGS, 0},
|
||||
{"0x1234567890acbdef0", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range
|
||||
{"18446744073709551616", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out or range (max value + 1)
|
||||
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
|
||||
};
|
||||
|
||||
TestCase<int8_t> kInt8TestCases[] = {
|
||||
{"0", OT_ERROR_NONE, 0},
|
||||
{"-1", OT_ERROR_NONE, -1},
|
||||
{"+74", OT_ERROR_NONE, 74},
|
||||
{"-0x12", OT_ERROR_NONE, -0x12},
|
||||
{"-0XB", OT_ERROR_NONE, -11},
|
||||
{"127", OT_ERROR_NONE, 127}, // Max `int8` value
|
||||
{"-128", OT_ERROR_NONE, -128}, // Min `int8` value
|
||||
{"128", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max value + 1)
|
||||
{"-129", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min value - 1)
|
||||
{"--1", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra sign
|
||||
{"+-2", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra sign
|
||||
{"++1", OT_ERROR_INVALID_ARGS, 0}, // Error case: Extra sign
|
||||
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
|
||||
};
|
||||
|
||||
TestCase<int16_t> kInt16TestCases[] = {
|
||||
{"-1", OT_ERROR_NONE, -1},
|
||||
{"+0x1234", OT_ERROR_NONE, 0x1234},
|
||||
{"-0X6E8", OT_ERROR_NONE, -0x6E8},
|
||||
{"32767", OT_ERROR_NONE, 32767}, // Max `int16` value
|
||||
{"0X7FFF", OT_ERROR_NONE, 0x7fff}, // Max `int16` value (hex value)
|
||||
{"-32768", OT_ERROR_NONE, -32768}, // Min `int16` value
|
||||
{"-0x8000", OT_ERROR_NONE, -0x8000}, // Min `int16` value (hex value)
|
||||
{"32768", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
|
||||
{"0X8000", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
|
||||
{"-32769", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
|
||||
{"-0x8001", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
|
||||
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list.
|
||||
};
|
||||
|
||||
TestCase<int32_t> kInt32TestCases[] = {
|
||||
{"-256", OT_ERROR_NONE, -256},
|
||||
{"+0x12345678", OT_ERROR_NONE, 0x12345678},
|
||||
{"-0X6677aB", OT_ERROR_NONE, -0X6677aB},
|
||||
{"2147483647", OT_ERROR_NONE, 2147483647},
|
||||
{"0x7fffFFFF", OT_ERROR_NONE, 0x7fffffff}, // Max `int32` value
|
||||
{"-2147483648", OT_ERROR_NONE, -2147483648}, // Min `int32` value
|
||||
{"2147483648", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
|
||||
{"0X80000000", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (max + 1)
|
||||
{"-2147483649", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
|
||||
{"-0x80000001", OT_ERROR_INVALID_ARGS, 0}, // Error case: Out of range (min - 1)
|
||||
{"", OT_ERROR_INVALID_ARGS, 0} // Empty string indicates end of the list
|
||||
};
|
||||
|
||||
VerifyParser<bool, ParseAsBool>(kBoolTestCases, "ParseAsBool", "%d");
|
||||
VerifyParser<uint8_t, ParseAsUint8>(kUint8TestCases, "ParseAsUint8", "0x%02x");
|
||||
VerifyParser<uint16_t, ParseAsUint16>(kUint16TestCases, "ParseAsUint16", "0x%04x");
|
||||
VerifyParser<uint32_t, ParseAsUint32>(kUint32TestCases, "ParseAsUint32", "0x%08x");
|
||||
VerifyParser<uint64_t, ParseAsUint64>(kUint64TestCases, "ParseAsUint64", "0x%016llx");
|
||||
VerifyParser<int8_t, ParseAsInt8>(kInt8TestCases, "ParseAsInt8", "%d");
|
||||
VerifyParser<int16_t, ParseAsInt16>(kInt16TestCases, "ParseAsInt16", "%d");
|
||||
VerifyParser<int32_t, ParseAsInt32>(kInt32TestCases, "ParseAsInt32", "%d");
|
||||
}
|
||||
|
||||
void TestParsingHexStrings(void)
|
||||
{
|
||||
const char kHexString[] = "DeadBeefCafeBabe";
|
||||
const uint8_t kParsedArray[] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe};
|
||||
|
||||
uint8_t buffer[sizeof(kParsedArray)];
|
||||
uint8_t buf3[3];
|
||||
uint16_t len;
|
||||
|
||||
// Verify `ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)`
|
||||
|
||||
buffer[0] = 0xff;
|
||||
SuccessOrQuit(ParseAsHexString("0", buffer, 1), "ParseAsHexString() failed");
|
||||
VerifyOrQuit(buffer[0] == 0, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
buffer[0] = 0;
|
||||
SuccessOrQuit(ParseAsHexString("7e", buffer, 1), "ParseAsHexString() failed");
|
||||
VerifyOrQuit(buffer[0] == 0x7e, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
VerifyOrQuit(ParseAsHexString("123", buffer, 1) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
|
||||
SuccessOrQuit(ParseAsHexString("123", buffer, 2), "ParseAsHexString() failed");
|
||||
VerifyOrQuit(buffer[0] == 1 && buffer[1] == 0x23, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
VerifyOrQuit(ParseAsHexString("123x", buffer, 2) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
|
||||
VerifyOrQuit(ParseAsHexString(" 123", buffer, 2) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
|
||||
|
||||
// Verify `ParseAsHexString<kMaxSize>()`
|
||||
|
||||
VerifyOrQuit(ParseAsHexString("1122", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
|
||||
VerifyOrQuit(ParseAsHexString("1122334", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
|
||||
VerifyOrQuit(ParseAsHexString("11223344", buf3) != OT_ERROR_NONE, "ParseAsHexString() passed with bad input");
|
||||
SuccessOrQuit(ParseAsHexString("abbade", buf3), "ParseAsHexString() failed");
|
||||
VerifyOrQuit(buf3[0] == 0xab && buf3[1] == 0xba && buf3[2] == 0xde, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
SuccessOrQuit(ParseAsHexString(kHexString, buffer), "ParseAsHexString failed");
|
||||
VerifyOrQuit(memcmp(buffer, kParsedArray, sizeof(buffer)) == 0, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
// Verify truncation
|
||||
|
||||
len = sizeof(buffer);
|
||||
|
||||
SuccessOrQuit(ParseAsHexString(kHexString, len, buffer), "ParseAsHexString failed");
|
||||
VerifyOrQuit(len == sizeof(kParsedArray), "ParseAsHexString() parsed incorrectly");
|
||||
VerifyOrQuit(memcmp(buffer, kParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
SuccessOrQuit(ParseAsHexString(kHexString + 1, len, buffer), "ParseAsHexString failed");
|
||||
VerifyOrQuit(len == sizeof(kParsedArray), "ParseAsHexString() parsed incorrectly");
|
||||
VerifyOrQuit(memcmp(buffer + 1, kParsedArray + 1, len - 1) == 0, "ParseAsHexString() parsed incorrectly");
|
||||
VerifyOrQuit(buffer[0] = 0xe, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
SuccessOrQuit(ParseAsHexString(kHexString + 2, len, buffer), "ParseAsHexString failed");
|
||||
VerifyOrQuit(len == sizeof(kParsedArray) - 1, "ParseAsHexString() parsed incorrectly");
|
||||
VerifyOrQuit(memcmp(buffer, kParsedArray + 1, len) == 0, "ParseAsHexString() parsed incorrectly");
|
||||
|
||||
len = sizeof(buffer) - 1;
|
||||
VerifyOrQuit(ParseAsHexString(kHexString, len, buffer, ot::Utils::CmdLineParser::kDisallowTruncate) !=
|
||||
OT_ERROR_NONE,
|
||||
"ParseAsHexString passed with bad input");
|
||||
len = sizeof(buffer) - 1;
|
||||
SuccessOrQuit(ParseAsHexString(kHexString, len, buffer, ot::Utils::CmdLineParser::kAllowTruncate),
|
||||
"ParseAsHexString failed");
|
||||
VerifyOrQuit(len == sizeof(buffer) - 1, "ParseAsHexString() parsed incorrectly");
|
||||
VerifyOrQuit(memcmp(buffer, kParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
TestParsingInts();
|
||||
TestParsingHexStrings();
|
||||
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user