/* * Copyright (c) 2017, 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 "common/code_utils.hpp" #include "instance/instance.hpp" #include "lib/spinel/spinel_decoder.hpp" #include "test_util.hpp" namespace ot { namespace Spinel { enum { kTestBufferSize = 800, }; void TestDecoder(void) { uint8_t buffer[kTestBufferSize]; Spinel::Decoder decoder; spinel_ssize_t frameLen; const bool kBool_1 = true; const bool kBool_2 = false; const uint8_t kUint8 = 0x42; const int8_t kInt8 = -73; const uint16_t kUint16 = 0xabcd; const int16_t kInt16 = -567; const uint32_t kUint32 = 0xdeadbeef; const int32_t kInt32 = -123455678L; const uint64_t kUint64 = 0xfe10dc32ba549876ULL; const int64_t kInt64 = -9197712039090021561LL; const unsigned int kUint_1 = 9; const unsigned int kUint_2 = 0xa3; const unsigned int kUint_3 = 0x8765; const unsigned int kUint_4 = SPINEL_MAX_UINT_PACKED - 1; const spinel_ipv6addr_t kIp6Addr = { {0x6B, 0x41, 0x65, 0x73, 0x42, 0x68, 0x61, 0x76, 0x54, 0x61, 0x72, 0x7A, 0x49, 0x69, 0x61, 0x4E}}; const spinel_eui48_t kEui48 = { {4, 8, 15, 16, 23, 42} // "Lost" EUI48! }; const spinel_eui64_t kEui64 = { {2, 3, 5, 7, 11, 13, 17, 19}, // "Prime" EUI64! }; const char kString_1[] = "OpenThread"; const char kString_2[] = ""; const uint16_t kData[] = {10, 20, 3, 15, 1000, 60, 16}; // ... then comes 17,18,19,20 :) bool b_1, b_2; uint8_t u8; int8_t i8; uint16_t u16; int16_t i16; uint32_t u32; int32_t i32; uint64_t u64; int64_t i64; unsigned int u_1, u_2, u_3, u_4; const spinel_ipv6addr_t *ip6Addr; const spinel_eui48_t *eui48; const spinel_eui64_t *eui64; const char *utf_1; const char *utf_2; const uint8_t *dataPtr_1; const uint8_t *dataPtr_2; uint16_t dataLen_1; uint16_t dataLen_2; memset(buffer, 0, sizeof(buffer)); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 1: Decoding simple types"); frameLen = spinel_datatype_pack( buffer, sizeof(buffer), (SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_INT32_S SPINEL_DATATYPE_UINT64_S SPINEL_DATATYPE_INT64_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UTF8_S SPINEL_DATATYPE_UTF8_S SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_S), kBool_1, kBool_2, kUint8, kInt8, kUint16, kInt16, kUint32, kInt32, kUint64, kInt64, kUint_1, kUint_2, kUint_3, kUint_4, &kIp6Addr, &kEui48, &kEui64, kString_1, kString_2, kData, sizeof(kData), kData, sizeof(kData)); DumpBuffer("Packed Spinel Frame", buffer, static_cast(frameLen)); decoder.Init(buffer, static_cast(frameLen)); VerifyOrQuit(decoder.GetFrame() == &buffer[0]); VerifyOrQuit(decoder.GetLength() == frameLen); VerifyOrQuit(decoder.GetReadLength() == 0); VerifyOrQuit(decoder.GetRemainingLength() == frameLen); VerifyOrQuit(decoder.IsAllRead() == false); SuccessOrQuit(decoder.ReadBool(b_1)); SuccessOrQuit(decoder.ReadBool(b_2)); SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.ReadInt8(i8)); SuccessOrQuit(decoder.ReadUint16(u16)); SuccessOrQuit(decoder.ReadInt16(i16)); SuccessOrQuit(decoder.ReadUint32(u32)); SuccessOrQuit(decoder.ReadInt32(i32)); SuccessOrQuit(decoder.ReadUint64(u64)); SuccessOrQuit(decoder.ReadInt64(i64)); // Check the state VerifyOrQuit(decoder.GetReadLength() != 0); VerifyOrQuit(decoder.GetRemainingLength() == frameLen - decoder.GetReadLength()); VerifyOrQuit(decoder.IsAllRead() == false); SuccessOrQuit(decoder.ReadUintPacked(u_1)); SuccessOrQuit(decoder.ReadUintPacked(u_2)); SuccessOrQuit(decoder.ReadUintPacked(u_3)); SuccessOrQuit(decoder.ReadUintPacked(u_4)); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); SuccessOrQuit(decoder.ReadEui48(eui48)); SuccessOrQuit(decoder.ReadEui64(eui64)); SuccessOrQuit(decoder.ReadUtf8(utf_1)); SuccessOrQuit(decoder.ReadUtf8(utf_2)); SuccessOrQuit(decoder.ReadDataWithLen(dataPtr_1, dataLen_1)); SuccessOrQuit(decoder.ReadData(dataPtr_2, dataLen_2)); VerifyOrQuit(decoder.GetReadLength() == frameLen); VerifyOrQuit(decoder.GetRemainingLength() == 0); VerifyOrQuit(decoder.IsAllRead() == true); VerifyOrQuit(b_1 == kBool_1); VerifyOrQuit(b_2 == kBool_2); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(i8 == kInt8); VerifyOrQuit(u16 == kUint16); VerifyOrQuit(i16 == kInt16); VerifyOrQuit(u32 == kUint32); VerifyOrQuit(i32 == kInt32); VerifyOrQuit(u64 == kUint64); VerifyOrQuit(i64 == kInt64); VerifyOrQuit(u_1 == kUint_1); VerifyOrQuit(u_2 == kUint_2); VerifyOrQuit(u_3 == kUint_3); VerifyOrQuit(u_4 == kUint_4); VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0); VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0); VerifyOrQuit(memcmp(eui64, &kEui64, sizeof(spinel_eui64_t)) == 0); VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0); VerifyOrQuit(memcmp(utf_2, kString_2, sizeof(kString_2)) == 0); VerifyOrQuit(dataLen_1 == sizeof(kData)); VerifyOrQuit(memcmp(dataPtr_1, &kData, sizeof(kData)) == 0); VerifyOrQuit(dataLen_2 == sizeof(kData)); VerifyOrQuit(memcmp(dataPtr_2, &kData, sizeof(kData)) == 0); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 2: Test Reset(), SavePosition(), ResetToSaved()"); // `ResetToSaved()` should fail if there is no saved position VerifyOrQuit(decoder.ResetToSaved() == OT_ERROR_INVALID_STATE); decoder.Reset(); VerifyOrQuit(decoder.GetFrame() == &buffer[0]); VerifyOrQuit(decoder.GetLength() == frameLen); VerifyOrQuit(decoder.GetReadLength() == 0); VerifyOrQuit(decoder.GetRemainingLength() == frameLen); VerifyOrQuit(decoder.IsAllRead() == false); SuccessOrQuit(decoder.ReadBool(b_1)); SuccessOrQuit(decoder.ReadBool(b_2)); SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.ReadInt8(i8)); SuccessOrQuit(decoder.ReadUint16(u16)); SuccessOrQuit(decoder.ReadInt16(i16)); SuccessOrQuit(decoder.ReadUint32(u32)); SuccessOrQuit(decoder.ReadInt32(i32)); // `ResetToSaved()` should fail if there is no saved position VerifyOrQuit(decoder.ResetToSaved() == OT_ERROR_INVALID_STATE); // Save position decoder.SavePosition(); SuccessOrQuit(decoder.ReadUint64(u64)); SuccessOrQuit(decoder.ReadInt64(i64)); SuccessOrQuit(decoder.ReadUintPacked(u_1)); SuccessOrQuit(decoder.ReadUintPacked(u_2)); SuccessOrQuit(decoder.ReadUintPacked(u_3)); SuccessOrQuit(decoder.ReadUintPacked(u_4)); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); VerifyOrQuit(b_1 == kBool_1); VerifyOrQuit(b_2 == kBool_2); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(i8 == kInt8); VerifyOrQuit(u16 == kUint16); VerifyOrQuit(i16 == kInt16); VerifyOrQuit(u32 == kUint32); VerifyOrQuit(i32 == kInt32); VerifyOrQuit(u64 == kUint64); VerifyOrQuit(i64 == kInt64); VerifyOrQuit(u_1 == kUint_1); VerifyOrQuit(u_2 == kUint_2); VerifyOrQuit(u_3 == kUint_3); VerifyOrQuit(u_4 == kUint_4); VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0); SuccessOrQuit(decoder.ResetToSaved()); SuccessOrQuit(decoder.ReadUint64(u64)); SuccessOrQuit(decoder.ReadInt64(i64)); SuccessOrQuit(decoder.ReadUintPacked(u_1)); SuccessOrQuit(decoder.ReadUintPacked(u_2)); SuccessOrQuit(decoder.ReadUintPacked(u_3)); SuccessOrQuit(decoder.ReadUintPacked(u_4)); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); VerifyOrQuit(u64 == kUint64); VerifyOrQuit(i64 == kInt64); VerifyOrQuit(u_1 == kUint_1); VerifyOrQuit(u_2 == kUint_2); VerifyOrQuit(u_3 == kUint_3); VerifyOrQuit(u_4 == kUint_4); VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0); // Go back to save position again. SuccessOrQuit(decoder.ResetToSaved()); SuccessOrQuit(decoder.ReadUint64(u64)); SuccessOrQuit(decoder.ReadInt64(i64)); SuccessOrQuit(decoder.ReadUintPacked(u_1)); SuccessOrQuit(decoder.ReadUintPacked(u_2)); SuccessOrQuit(decoder.ReadUintPacked(u_3)); SuccessOrQuit(decoder.ReadUintPacked(u_4)); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); VerifyOrQuit(u64 == kUint64); VerifyOrQuit(i64 == kInt64); VerifyOrQuit(u_1 == kUint_1); VerifyOrQuit(u_2 == kUint_2); VerifyOrQuit(u_3 == kUint_3); VerifyOrQuit(u_4 == kUint_4); VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0); // Ensure saved position is cleared when decoder is reset or re-initialized. decoder.Reset(); // `ResetToSaved()` should fail if there is no saved position VerifyOrQuit(decoder.ResetToSaved() == OT_ERROR_INVALID_STATE); decoder.SavePosition(); SuccessOrQuit(decoder.ResetToSaved()); decoder.Init(buffer, static_cast(frameLen)); VerifyOrQuit(decoder.ResetToSaved() == OT_ERROR_INVALID_STATE); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 3: Test decoding a single simple struct."); frameLen = spinel_datatype_pack(buffer, sizeof(buffer), (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_STRUCT_S( SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_UINT_PACKED_S) SPINEL_DATATYPE_INT16_S ), kUint8, kUint32, &kEui48, kUint_3, kInt16); DumpBuffer("Packed Spinel Frame (single struct)", buffer, static_cast(frameLen)); decoder.Init(buffer, static_cast(frameLen)); SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint32(u32)); SuccessOrQuit(decoder.ReadEui48(eui48)); SuccessOrQuit(decoder.ReadUintPacked(u_3)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadInt16(i16)); VerifyOrQuit(decoder.IsAllRead() == true); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(i16 == kInt16); VerifyOrQuit(u32 == kUint32); VerifyOrQuit(u_3 == kUint_3); VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 4: Test partial struct read"); // Re-use same frame as the previous test. decoder.Init(buffer, static_cast(frameLen)); SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint32(u32)); // Skip the remaining fields in the struct } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadInt16(i16)); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(i16 == kInt16); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 5: Test `GetRemainingLengthInStruct()` and `IsAllReadInStruct`() in and out of an struct"); // Re-use same frame as the previous test. decoder.Init(buffer, static_cast(frameLen)); VerifyOrQuit(decoder.GetFrame() == &buffer[0]); VerifyOrQuit(decoder.GetLength() == frameLen); VerifyOrQuit(decoder.GetReadLength() == 0); VerifyOrQuit(decoder.GetRemainingLength() == frameLen); VerifyOrQuit(decoder.IsAllRead() == false); // When not in an struct, `etRemainingLengthInStruct()` should consider the whole frame. VerifyOrQuit(decoder.GetRemainingLengthInStruct() == frameLen); VerifyOrQuit(decoder.IsAllReadInStruct() == false); SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.OpenStruct()); { VerifyOrQuit(decoder.IsAllReadInStruct() == false); SuccessOrQuit(decoder.ReadUint32(u32)); SuccessOrQuit(decoder.ReadEui48(eui48)); SuccessOrQuit(decoder.ReadUintPacked(u_3)); VerifyOrQuit(decoder.IsAllReadInStruct() == true); VerifyOrQuit(decoder.GetRemainingLengthInStruct() == 0); // Try reading beyond end of the struct and ensure it fails. VerifyOrQuit(decoder.ReadUint8(u8) == OT_ERROR_PARSE); // `ReadData()` at end of struct should still succeed but return zero as the data length. SuccessOrQuit(decoder.ReadData(dataPtr_1, dataLen_1)); VerifyOrQuit(dataLen_1 == 0); } SuccessOrQuit(decoder.CloseStruct()); VerifyOrQuit(decoder.IsAllReadInStruct() == false); SuccessOrQuit(decoder.ReadInt16(i16)); VerifyOrQuit(decoder.IsAllRead() == true); VerifyOrQuit(decoder.GetRemainingLengthInStruct() == 0); VerifyOrQuit(decoder.IsAllReadInStruct() == true); // `ReadData()` at end of frame should still succeed but return zero as the data length. SuccessOrQuit(decoder.ReadData(dataPtr_1, dataLen_1)); VerifyOrQuit(dataLen_1 == 0); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(i16 == kInt16); VerifyOrQuit(u32 == kUint32); VerifyOrQuit(u_3 == kUint_3); VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 6: Test multiple nested structs"); frameLen = spinel_datatype_pack( buffer, sizeof(buffer), (SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UTF8_S SPINEL_DATATYPE_STRUCT_S( SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_IPv6ADDR_S) SPINEL_DATATYPE_UINT16_S) SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_UINT32_S) SPINEL_DATATYPE_INT32_S), kUint8, kString_1, kBool_1, &kIp6Addr, kUint16, &kEui48, kUint32, kInt32); DumpBuffer("Packed Spinel Frame (multiple struct)", buffer, static_cast(frameLen)); decoder.Init(buffer, static_cast(frameLen)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.ReadUtf8(utf_1)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadBool(b_1)); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadUint16(u16)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadEui48(eui48)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint32(u32)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadInt32(i32)); VerifyOrQuit(decoder.GetReadLength() == frameLen); VerifyOrQuit(decoder.GetRemainingLength() == 0); VerifyOrQuit(decoder.IsAllRead() == true); VerifyOrQuit(b_1 == kBool_1); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(u16 == kUint16); VerifyOrQuit(u32 == kUint32); VerifyOrQuit(i32 == kInt32); VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0); VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0); VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 7: Test `SavePosition()`, `ResetToSaved()` for nested structs"); // Re-use same frame as the previous test. decoder.Init(buffer, static_cast(frameLen)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint8(u8)); decoder.SavePosition(); SuccessOrQuit(decoder.ReadUtf8(utf_1)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadBool(b_1)); } // Verify the read content so far. VerifyOrQuit(u8 == kUint8); VerifyOrQuit(b_1 == kBool_1); VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0); // Do not close the inner struct and jump to previously saved position and re-read the content. SuccessOrQuit(decoder.ResetToSaved()); SuccessOrQuit(decoder.ReadUtf8(utf_1)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadBool(b_1)); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadUint16(u16)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadEui48(eui48)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint32(u32)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadInt32(i32)); VerifyOrQuit(decoder.GetReadLength() == frameLen); VerifyOrQuit(decoder.GetRemainingLength() == 0); VerifyOrQuit(decoder.IsAllRead() == true); VerifyOrQuit(b_1 == kBool_1); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(u16 == kUint16); VerifyOrQuit(u32 == kUint32); VerifyOrQuit(i32 == kInt32); VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0); VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0); VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 8: Test saving position at start of an open struct"); // Re-use same frame as the previous test. decoder.Init(buffer, static_cast(frameLen)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.ReadUtf8(utf_1)); SuccessOrQuit(decoder.OpenStruct()); { // Save position at start of the struct decoder.SavePosition(); SuccessOrQuit(decoder.ReadBool(b_1)); // Verify the read content so far. VerifyOrQuit(u8 == kUint8); VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0); VerifyOrQuit(b_1 == kBool_1); // Do not close the struct and jump to the previously saved position and re-read the content. SuccessOrQuit(decoder.ResetToSaved()); SuccessOrQuit(decoder.ReadBool(b_1)); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadUint16(u16)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadEui48(eui48)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint32(u32)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadInt32(i32)); VerifyOrQuit(decoder.GetReadLength() == frameLen); VerifyOrQuit(decoder.GetRemainingLength() == 0); VerifyOrQuit(decoder.IsAllRead() == true); VerifyOrQuit(b_1 == kBool_1); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(u16 == kUint16); VerifyOrQuit(u32 == kUint32); VerifyOrQuit(i32 == kInt32); VerifyOrQuit(memcmp(ip6Addr, &kIp6Addr, sizeof(spinel_ipv6addr_t)) == 0); VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0); VerifyOrQuit(memcmp(utf_1, kString_1, sizeof(kString_1)) == 0); printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 9: Test `ResetToSaved()` failure case (jumping back to a saved position closed struct)."); // Re-use same frame as the previous test. decoder.Init(buffer, static_cast(frameLen)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.ReadUtf8(utf_1)); SuccessOrQuit(decoder.OpenStruct()); { SuccessOrQuit(decoder.ReadBool(b_1)); decoder.SavePosition(); SuccessOrQuit(decoder.ReadIp6Address(ip6Addr)); } SuccessOrQuit(decoder.CloseStruct()); SuccessOrQuit(decoder.ReadUint16(u16)); // `ResetToSaved()` should fail since the enclosing struct for the saved position is closed. VerifyOrQuit(decoder.ResetToSaved() == OT_ERROR_INVALID_STATE); } printf(" -- PASS\n"); printf("\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); printf("\nTest 10: Testing error cases and failures. (e.g., wrong struct length)."); frameLen = spinel_datatype_pack(buffer, sizeof(buffer), (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT16_S // Treat this as struct length SPINEL_DATATYPE_BOOL_S), kUint8, 10, kBool_1); DumpBuffer("Packed Spinel Frame (incorrect format)", buffer, static_cast(frameLen)); decoder.Init(buffer, static_cast(frameLen)); decoder.SavePosition(); SuccessOrQuit(decoder.ReadUint8(u8)); VerifyOrQuit(u8 == kUint8); // `OpenStruct()` should fail, since it expects a length 10 but there are not enough // bytes in the frame. VerifyOrQuit(decoder.OpenStruct() == OT_ERROR_PARSE); SuccessOrQuit(decoder.ResetToSaved()); SuccessOrQuit(decoder.ReadUint8(u8)); VerifyOrQuit(u8 == kUint8); VerifyOrQuit(decoder.ReadDataWithLen(dataPtr_1, dataLen_1) == OT_ERROR_PARSE); SuccessOrQuit(decoder.ResetToSaved()); SuccessOrQuit(decoder.ReadUint8(u8)); SuccessOrQuit(decoder.ReadUint16(u16)); SuccessOrQuit(decoder.ReadBool(b_1)); // Try reading beyond end of frame. VerifyOrQuit(decoder.ReadUint8(u8) == OT_ERROR_PARSE); printf(" -- PASS\n"); } } // namespace Spinel } // namespace ot int main(void) { ot::Spinel::Decoder(); printf("\nAll tests passed.\n"); return 0; }