| /* |
| * 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.hpp" |
| |
| using ot::Utils::CmdLineParser::ParseAsBool; |
| using ot::Utils::CmdLineParser::ParseAsHexString; |
| using ot::Utils::CmdLineParser::ParseAsHexStringSegment; |
| using ot::Utils::CmdLineParser::ParseAsInt16; |
| using ot::Utils::CmdLineParser::ParseAsInt32; |
| using ot::Utils::CmdLineParser::ParseAsInt8; |
| 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 kEvenHexString[] = "DeadBeefCafeBabe"; |
| const uint8_t kEvenParsedArray[] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe}; |
| |
| const char kOddHexString[] = "abcdef9876543"; |
| const uint8_t kOddParsedArray[] = {0xa, 0xbc, 0xde, 0xf9, 0x87, 0x65, 0x43}; |
| |
| uint8_t buffer[sizeof(kEvenParsedArray)]; |
| uint8_t buf3[3]; |
| uint16_t len; |
| const char * string; |
| const uint8_t *bufPtr; |
| |
| // Verify `ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)` |
| |
| buffer[0] = 0xff; |
| SuccessOrQuit(ParseAsHexString("0", buffer, 1)); |
| VerifyOrQuit(buffer[0] == 0, "ParseAsHexString() parsed incorrectly"); |
| |
| buffer[0] = 0; |
| SuccessOrQuit(ParseAsHexString("7e", buffer, 1)); |
| 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)); |
| 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)); |
| |
| VerifyOrQuit(buf3[0] == 0xab && buf3[1] == 0xba && buf3[2] == 0xde, "ParseAsHexString() parsed incorrectly"); |
| SuccessOrQuit(ParseAsHexString("012345", buf3)); |
| VerifyOrQuit(buf3[0] == 0x01 && buf3[1] == 0x23 && buf3[2] == 0x45, "ParseAsHexString() parsed incorrectly"); |
| SuccessOrQuit(ParseAsHexString("12345", buf3), "ParseAsHexString() failed with odd length"); |
| VerifyOrQuit(buf3[0] == 0x01 && buf3[1] == 0x23 && buf3[2] == 0x45, "ParseAsHexString() parsed incorrectly"); |
| |
| SuccessOrQuit(ParseAsHexString(kEvenHexString, buffer), "ParseAsHexString failed"); |
| VerifyOrQuit(memcmp(buffer, kEvenParsedArray, sizeof(buffer)) == 0, "ParseAsHexString() parsed incorrectly"); |
| |
| // Verify `ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer)` |
| |
| printf("----------------------------------------------------------\n"); |
| len = sizeof(buffer); |
| |
| SuccessOrQuit(ParseAsHexString(kEvenHexString, len, buffer)); |
| VerifyOrQuit(len == sizeof(kEvenParsedArray), "ParseAsHexString() parsed incorrectly"); |
| VerifyOrQuit(memcmp(buffer, kEvenParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly"); |
| DumpBuffer(kEvenHexString, buffer, len); |
| |
| SuccessOrQuit(ParseAsHexString(kOddHexString, len, buffer)); |
| VerifyOrQuit(len == sizeof(kOddParsedArray), "ParseAsHexString() parsed incorrectly"); |
| VerifyOrQuit(memcmp(buffer, kOddParsedArray, len) == 0, "ParseAsHexString() parsed incorrectly"); |
| DumpBuffer(kOddHexString, buffer, len); |
| |
| // Verify `ParseAsHexStringSegement()` |
| |
| printf("----------------------------------------------------------\n"); |
| |
| for (uint8_t testIter = 0; testIter <= 1; testIter++) |
| { |
| for (uint8_t segmentLen = 1; segmentLen <= sizeof(buffer); segmentLen++) |
| { |
| if (testIter == 0) |
| { |
| string = kEvenHexString; |
| bufPtr = kEvenParsedArray; |
| } |
| else |
| { |
| string = kOddHexString; |
| bufPtr = kOddParsedArray; |
| } |
| |
| len = segmentLen; |
| |
| printf("\"%s\" segLen:%d -> ", string, segmentLen); |
| |
| while (true) |
| { |
| otError error = ParseAsHexStringSegment(string, len, buffer); |
| |
| printf("%d (\"%s\") ", len, string); |
| |
| if (error == OT_ERROR_NONE) |
| { |
| VerifyOrQuit(len <= segmentLen, "ParseAsHexStringSegment() parsed incorrectly"); |
| VerifyOrQuit(memcmp(buffer, bufPtr, len) == 0, "ParseAsHexStringSegment() parsed incorrectly"); |
| VerifyOrQuit(*string == '\0', |
| "ParseAsHexStringSegment() failed to update string pointer correctly"); |
| break; |
| } |
| |
| VerifyOrQuit(error == OT_ERROR_PENDING, "ParseAsHexStringSegment() failed"); |
| VerifyOrQuit(len == segmentLen, "ParseAsHexStringSegment() parsed incorrectly"); |
| VerifyOrQuit(memcmp(buffer, bufPtr, len) == 0, "ParseAsHexStringSegment() parsed incorrectly"); |
| bufPtr += len; |
| } |
| |
| printf("\n"); |
| } |
| } |
| } |
| |
| int main(void) |
| { |
| TestParsingInts(); |
| TestParsingHexStrings(); |
| |
| printf("All tests passed\n"); |
| return 0; |
| } |