diff --git a/src/lib/support/jsontlv/JsonToTlv.cpp b/src/lib/support/jsontlv/JsonToTlv.cpp index 387c5f94594da1..0cc816133be9e7 100644 --- a/src/lib/support/jsontlv/JsonToTlv.cpp +++ b/src/lib/support/jsontlv/JsonToTlv.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -179,26 +180,34 @@ bool CompareByTag(const ElementContext & a, const ElementContext & b) return IsContextTag(a.tag); } -CHIP_ERROR InternalConvertTlvTag(const uint64_t tagNumber, TLV::Tag & tag, const uint32_t profileId = kTemporaryImplicitProfileId) +// The profileId parameter is used when encoding a tag for a TLV element to specify the profile that the tag belongs to. +// If the vendor ID is zero but the tag ID does not fit within an 8-bit value, the function uses Implicit Profile Tag. +// Here, the kTemporaryImplicitProfileId serves as a default value for cases where no explicit profile ID is provided by +// the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead +// associated with a temporary implicit profile ID (0xFF01). +CHIP_ERROR InternalConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag, const uint32_t profileId = kTemporaryImplicitProfileId) { - if (tagNumber <= UINT8_MAX) + uint16_t vendor_id = static_cast(tagNumber >> 16); + uint16_t tag_id = static_cast(tagNumber & 0xFFFF); + + if (vendor_id != 0) { - tag = TLV::ContextTag(static_cast(tagNumber)); + tag = TLV::ProfileTag(vendor_id, /*profileNum=*/0, tag_id); } - else if (tagNumber <= UINT32_MAX) + else if (tag_id <= UINT8_MAX) { - tag = TLV::ProfileTag(profileId, static_cast(tagNumber)); + tag = TLV::ContextTag(static_cast(tagNumber)); } else { - return CHIP_ERROR_INVALID_ARGUMENT; + tag = TLV::ProfileTag(profileId, tagNumber); } return CHIP_NO_ERROR; } CHIP_ERROR ParseJsonName(const std::string name, ElementContext & elementCtx, uint32_t implicitProfileId) { - uint64_t tagNumber = 0; + uint32_t tagNumber = 0; const char * elementType = nullptr; std::vector nameFields = SplitIntoFieldsBySeparator(name, ':'); TLV::Tag tag = TLV::AnonymousTag(); @@ -208,13 +217,27 @@ CHIP_ERROR ParseJsonName(const std::string name, ElementContext & elementCtx, ui if (nameFields.size() == 2) { VerifyOrReturnError(IsUnsignedInteger(nameFields[0]), CHIP_ERROR_INVALID_ARGUMENT); - tagNumber = std::strtoull(nameFields[0].c_str(), nullptr, 10); + + char * endPtr; + errno = 0; + unsigned long result = strtoul(nameFields[0].c_str(), &endPtr, 10); + VerifyOrReturnError(nameFields[0].c_str() != endPtr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((errno != ERANGE && result <= UINT32_MAX), CHIP_ERROR_INVALID_ARGUMENT); + + tagNumber = static_cast(result); elementType = nameFields[1].c_str(); } else if (nameFields.size() == 3) { VerifyOrReturnError(IsUnsignedInteger(nameFields[1]), CHIP_ERROR_INVALID_ARGUMENT); - tagNumber = std::strtoull(nameFields[1].c_str(), nullptr, 10); + + char * endPtr; + errno = 0; + unsigned long result = strtoul(nameFields[1].c_str(), &endPtr, 10); + VerifyOrReturnError(nameFields[1].c_str() != endPtr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError((errno != ERANGE && result <= UINT32_MAX), CHIP_ERROR_INVALID_ARGUMENT); + + tagNumber = static_cast(result); elementType = nameFields[2].c_str(); } else @@ -459,10 +482,19 @@ CHIP_ERROR JsonToTlv(const std::string & jsonString, TLV::TLVWriter & writer) ElementContext elementCtx; elementCtx.type = { TLV::kTLVType_Structure, false }; + + // Use kTemporaryImplicitProfileId as the default value for cases where no explicit implicit profile ID is provided by + // the caller. This allows for the encoding of tags that are not vendor-specific or context-specific but are instead + // associated with a temporary implicit profile ID (0xFF01). + if (writer.ImplicitProfileId == TLV::kProfileIdNotSpecified) + { + writer.ImplicitProfileId = kTemporaryImplicitProfileId; + } + return EncodeTlvElement(json, writer, elementCtx); } -CHIP_ERROR ConvertTlvTag(const uint64_t tagNumber, TLV::Tag & tag) +CHIP_ERROR ConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag) { return InternalConvertTlvTag(tagNumber, tag); } diff --git a/src/lib/support/jsontlv/JsonToTlv.h b/src/lib/support/jsontlv/JsonToTlv.h index 287f0d7279f645..9b0ff194fa8182 100644 --- a/src/lib/support/jsontlv/JsonToTlv.h +++ b/src/lib/support/jsontlv/JsonToTlv.h @@ -33,10 +33,14 @@ CHIP_ERROR JsonToTlv(const std::string & jsonString, MutableByteSpan & tlv); CHIP_ERROR JsonToTlv(const std::string & jsonString, TLV::TLVWriter & writer); /* - * Convert a uint64_t tagNumber to a TLV tag. When tagNumber is less than or equal to UINT8_MAX, - * the tag is encoded using ContextTag. When tagNumber is larger than UINT8_MAX and less than or equal to UINT32_MAX, - * the tag is encoded using an implicit profile tag. + * Convert a uint32_t tagNumber (from MEI) to a TLV tag. + * The upper 16 bits of tag_number represent the vendor_id. + * The lower 16 bits of tag_number represent the tag_id. + * When the MEI prefix encodes a standard/scoped source, the tag is encoded using ContextSpecific tag if tag_id is less than or + * equal to UINT8_MAX, and ImplicitProfile tag if tag_id is larger than UINT8_MAX. When the MEI prefix encodes a manufacturer code, + * the tag is encoded using FullyQualified_6Bytes tag, the Vendor ID SHALL be set to the manufacturer code, the profile number set + * to 0 and the tag number set to the MEI suffix. */ -CHIP_ERROR ConvertTlvTag(const uint64_t tagNumber, TLV::Tag & tag); +CHIP_ERROR ConvertTlvTag(uint32_t tagNumber, TLV::Tag & tag); } // namespace chip diff --git a/src/lib/support/jsontlv/TlvToJson.cpp b/src/lib/support/jsontlv/TlvToJson.cpp index 9bafeccf431b2c..3169dbb79faa27 100644 --- a/src/lib/support/jsontlv/TlvToJson.cpp +++ b/src/lib/support/jsontlv/TlvToJson.cpp @@ -108,14 +108,12 @@ struct JsonObjectElementContext { if (TLV::ProfileIdFromTag(tag) == implicitProfileId) { - // Explicit assume implicit tags are just things we want - // 32-bit numbers for str = std::to_string(TLV::TagNumFromTag(tag)); } else { - // UNEXPECTED, create a full 64-bit number here - str = std::to_string(TLV::ProfileIdFromTag(tag)) + "/" + std::to_string(TLV::TagNumFromTag(tag)); + uint32_t tagNumber = (static_cast(TLV::VendorIdFromTag(tag)) << 16) | TLV::TagNumFromTag(tag); + str = std::to_string(tagNumber); } } str = str + ":" + GetJsonElementStrFromType(type); @@ -173,11 +171,8 @@ CHIP_ERROR TlvStructToJson(TLV::TLVReader & reader, Json::Value & jsonObj) TLV::Tag tag = reader.GetTag(); VerifyOrReturnError(TLV::IsContextTag(tag) || TLV::IsProfileTag(tag), CHIP_ERROR_INVALID_TLV_TAG); - // Profile tags are expected to be implicit profile tags and they are - // used to encode > 8bit values from json - if (TLV::IsProfileTag(tag)) + if (TLV::IsProfileTag(tag) && TLV::VendorIdFromTag(tag) == 0) { - VerifyOrReturnError(TLV::ProfileIdFromTag(tag) == reader.ImplicitProfileId, CHIP_ERROR_INVALID_TLV_TAG); VerifyOrReturnError(TLV::TagNumFromTag(tag) > UINT8_MAX, CHIP_ERROR_INVALID_TLV_TAG); } diff --git a/src/lib/support/tests/TestJsonToTlv.cpp b/src/lib/support/tests/TestJsonToTlv.cpp index c6f5df36333238..539d2720caf191 100644 --- a/src/lib/support/tests/TestJsonToTlv.cpp +++ b/src/lib/support/tests/TestJsonToTlv.cpp @@ -300,7 +300,7 @@ void Test32BitConvert(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(kImplicitProfileId, 0xFEDCBA98u)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag((4275878552 >> 16) & 0xFFFF, 0, 4275878552 & 0xFFFF)) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, value == 321); @@ -312,6 +312,76 @@ void Test32BitConvert(nlTestSuite * inSuite, void * inContext) // FIXME: implement } +void TestMEIConvert(nlTestSuite * inSuite, void * inContext) +{ + TLV::TLVReader reader; + TLV::TLVType tlvType; + int32_t value = 0; + + // Vendor ID = 1, Tag ID = 0 + { + SetupWriters(); + JsonToTlv("{\"65536:INT\": 321}", gWriter1); + NL_TEST_ASSERT(inSuite, gWriter1.Finalize() == CHIP_NO_ERROR); + + reader.Init(gBuf1, gWriter1.GetLengthWritten()); + reader.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); + NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(1, 0, 0)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); + NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, value == 321); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + NL_TEST_ASSERT(inSuite, reader.ExitContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + } + + // Vendor ID = 0xFFFF, Tag ID = 0 + { + SetupWriters(); + JsonToTlv("{\"4294901760:INT\": 123}", gWriter1); + NL_TEST_ASSERT(inSuite, gWriter1.Finalize() == CHIP_NO_ERROR); + + reader.Init(gBuf1, gWriter1.GetLengthWritten()); + reader.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); + NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(0xFFFF, 0, 0)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); + NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, value == 123); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + NL_TEST_ASSERT(inSuite, reader.ExitContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + } + + // Vendor ID = 0xFFFF, Tag ID = 0xFFFF + { + SetupWriters(); + JsonToTlv("{\"4294967295:INT\": 123}", gWriter1); + NL_TEST_ASSERT(inSuite, gWriter1.Finalize() == CHIP_NO_ERROR); + + reader.Init(gBuf1, gWriter1.GetLengthWritten()); + reader.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(inSuite, reader.Next(TLV::AnonymousTag()) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_Structure); + NL_TEST_ASSERT(inSuite, reader.EnterContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next(TLV::ProfileTag(0xFFFF, 0, 0xFFFF)) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.GetType() == TLV::kTLVType_SignedInteger); + NL_TEST_ASSERT(inSuite, reader.Get(value) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, value == 123); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + NL_TEST_ASSERT(inSuite, reader.ExitContainer(tlvType) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, reader.Next() == CHIP_END_OF_TLV); + } +} + int Initialize(void * apSuite) { VerifyOrReturnError(chip::Platform::MemoryInit() == CHIP_NO_ERROR, FAILURE); @@ -329,6 +399,7 @@ const nlTest sTests[] = { NL_TEST_DEF("TestConverter", TestConverter), NL_TEST_DEF("Test32BitConvert", Test32BitConvert), + NL_TEST_DEF("TestMEIConvert", TestMEIConvert), NL_TEST_SENTINEL() }; // clang-format on diff --git a/src/lib/support/tests/TestJsonToTlvToJson.cpp b/src/lib/support/tests/TestJsonToTlvToJson.cpp index b14d84ceed4e03..576203461b5384 100644 --- a/src/lib/support/tests/TestJsonToTlvToJson.cpp +++ b/src/lib/support/tests/TestJsonToTlvToJson.cpp @@ -705,7 +705,8 @@ void TestConverter_Array_Empty_ImplicitProfileTag4(nlTestSuite * inSuite, void * NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == - writer.StartContainer(TLV::ProfileTag(kImplicitProfileId, 1000000), TLV::kTLVType_Array, containerType2)); + writer.StartContainer(TLV::ProfileTag((1000000 >> 16) & 0xFFFF, 0, 1000000 & 0xFFFF), TLV::kTLVType_Array, + containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Finalize()); @@ -1070,7 +1071,8 @@ void TestConverter_Array_Strings(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == - writer.StartContainer(TLV::ProfileTag(kImplicitProfileId, 100000), TLV::kTLVType_Array, containerType2)); + writer.StartContainer(TLV::ProfileTag((100000 >> 16) & 0xFFFF, 0, 100000 & 0xFFFF), TLV::kTLVType_Array, + containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.PutString(TLV::AnonymousTag(), "ABC")); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.PutString(TLV::AnonymousTag(), "Options")); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.PutString(TLV::AnonymousTag(), "more")); @@ -1202,11 +1204,10 @@ void TestConverter_Struct_MixedTags(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::ContextTag(0), TLV::kTLVType_Structure, containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ContextTag(255), static_cast(42))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0x0001u, 0, 0), static_cast(345678))); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 256), static_cast(17000))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFFFFu, 0, 0xFFFFu), static_cast(500000000000))); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 65535), static_cast(1))); - NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 65536), static_cast(345678))); - NL_TEST_ASSERT( - gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(kImplicitProfileId, 4294967295), static_cast(500000000000))); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType2)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Finalize()); @@ -1706,7 +1707,7 @@ void TestConverter_TlvToJson_ErrorCases(nlTestSuite * inSuite, void * inContext) uint8_t buf9[32]; writer.Init(buf9); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); - NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xAA55FEED, 234), static_cast(42))); + NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFEED, 234), static_cast(42))); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); NL_TEST_ASSERT(inSuite, CHIP_NO_ERROR == writer.Finalize()); ByteSpan useFullyQualifiedTag(buf9, writer.GetLengthWritten()); @@ -1773,10 +1774,6 @@ void TestConverter_JsonToTlv_ErrorCases(nlTestSuite * inSuite, void * inContext) " \"UINT\" : 42\n" "}\n"; - std::string invalidNameTagValueTooBig = "{\n" - " \"invalid:4294967296:UINT\" : 42\n" - "}\n"; - std::string invalidNameWithNegativeTag = "{\n" " \"-1:UINT\" : 42\n" "}\n"; @@ -1814,7 +1811,6 @@ void TestConverter_JsonToTlv_ErrorCases(nlTestSuite * inSuite, void * inContext) { arrayElementsWithName, CHIP_ERROR_INTERNAL, "Array Elements With Json Name" }, { invalidNameWithoutTagField, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name String Without Tag Field" }, { invalidNameWithoutTagField2, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name String Without Tag Field 2" }, - { invalidNameTagValueTooBig, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name String Tag Value Larger than UINT32_MAX" }, { invalidNameWithNegativeTag, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name With Negative Tag Value" }, { invalidNameWithInvalidTypeField, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Name With Invalid Type Field" }, { invalidBytesBase64Value1, CHIP_ERROR_INVALID_ARGUMENT, "Invalid Base64 Encoding: Invalid Character" }, @@ -1834,6 +1830,40 @@ void TestConverter_JsonToTlv_ErrorCases(nlTestSuite * inSuite, void * inContext) } } +// Full Qualified Profile tags, Unsigned Integer structure: {65536 = 42, 4294901760 = 17000, 4294967295 = 500000000000} +void TestConverter_Struct_MEITags(nlTestSuite * inSuite, void * inContext) +{ + gSuite = inSuite; + + uint8_t buf[256]; + TLV::TLVWriter writer; + TLV::TLVType containerType; + TLV::TLVType containerType2; + + writer.Init(buf); + writer.ImplicitProfileId = kImplicitProfileId; + + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, containerType)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.StartContainer(TLV::ContextTag(0), TLV::kTLVType_Structure, containerType2)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFFFFu, 0, 0), static_cast(17000))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0x0001u, 0, 0), static_cast(42))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Put(TLV::ProfileTag(0xFFFFu, 0, 0xFFFFu), static_cast(500000000000))); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType2)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.EndContainer(containerType)); + NL_TEST_ASSERT(gSuite, CHIP_NO_ERROR == writer.Finalize()); + + std::string jsonString = "{\n" + " \"0:STRUCT\" : {\n" + " \"65536:UINT\" : 42,\n" + " \"4294901760:UINT\" : 17000,\n" + " \"4294967295:UINT\" : \"500000000000\"\n" + " }\n" + "}\n"; + + ByteSpan tlvSpan(buf, writer.GetLengthWritten()); + CheckValidConversion(jsonString, tlvSpan, jsonString); +} + int Initialize(void * apSuite) { VerifyOrReturnError(chip::Platform::MemoryInit() == CHIP_NO_ERROR, FAILURE); @@ -1899,6 +1929,7 @@ const nlTest sTests[] = { NL_TEST_DEF("Test Json Tlv Converter - Complex Structure from the README File", TestConverter_Structure_FromReadme), NL_TEST_DEF("Test Json Tlv Converter - Tlv to Json Error Cases", TestConverter_TlvToJson_ErrorCases), NL_TEST_DEF("Test Json Tlv Converter - Json To Tlv Error Cases", TestConverter_JsonToTlv_ErrorCases), + NL_TEST_DEF("Test Json Tlv Converter - Structure with MEI Elements", TestConverter_Struct_MEITags), NL_TEST_SENTINEL() };