diff --git a/src/ua_types_encoding_xml.c b/src/ua_types_encoding_xml.c index 999ac3dbb..b668efd9c 100644 --- a/src/ua_types_encoding_xml.c +++ b/src/ua_types_encoding_xml.c @@ -109,7 +109,17 @@ XmlEncTypeDef xmlEncTypeDefs[UA_DATATYPEKINDS] = { "" "", 330}, /* ExtensionObject */ {"", 0}, /* DataValue */ - {"", 0}, /* Variant */ + {"" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "", 248}, /* Variant */ {"", 0}, /* DiagnosticInfo */ {"", 0}, /* Decimal */ {"", 0}, /* Enum */ @@ -146,6 +156,43 @@ static const char* UA_XML_EXTENSIONOBJECT_TYPEID = "TypeId"; // NodeId static const char* UA_XML_EXTENSIONOBJECT_BODY = "Body"; static const char* UA_XML_EXTENSIONOBJECT_BYTESTRING = "ByteString"; +/* Variant */ +static const char* UA_XML_VARIANT_VALUE = "Value"; + +const char* xsdTypeNames[UA_DATATYPEKINDS] = { + "xs:boolean", /* Boolean */ + "xs:byte", /* SByte */ + "xs:unsignedByte", /* Byte */ + "xs:short", /* Int16 */ + "xs:unsignedShort", /* UInt16 */ + "xs:int", /* Int32 */ + "xs:unsignedInt", /* UInt32 */ + "xs:long", /* Int64 */ + "xs:unsignedLong", /* UInt64 */ + "xs:float", /* Float */ + "xs:double", /* Double */ + "xs:string", /* String */ + "xs:dateTime", /* DateTime */ + "tns:Guid", /* Guid */ + "xs:base64Binary", /* ByteString */ + "tns:XmlElement", /* XmlElement */ + "tns:NodeId", /* NodeId */ + "tns:ExpandedNodeId", /* ExpandedNodeId */ + "tns:StatusCode", /* StatusCode */ + "tns:QualifiedName", /* QualifiedName */ + "tns:LocalizedText", /* LocalizedText */ + "tns:ExtensionObject", /* ExtensionObject */ + "tns:DataValue", /* DataValue */ + "tns:Variant", /* Variant */ + "tns:DiagnosticInfo", /* DiagnosticInfo */ + "tns:Decimal", /* Decimal */ + NULL, /* Enum */ + NULL, /* Structure */ + NULL, /* Structure with optional fields */ + NULL, /* Union */ + NULL /* BitfieldCluster */ +}; + /************/ /* Encoding */ /************/ @@ -160,6 +207,15 @@ static const char* UA_XML_EXTENSIONOBJECT_BYTESTRING = "ByteString"; (type->typeKind < UA_DATATYPEKIND_GUID || \ type->typeKind == UA_DATATYPEKIND_BYTESTRING) +static bool +isNullXml(const void *p, const UA_DataType *type) { + if(UA_DataType_isNumeric(type) || type->typeKind == UA_DATATYPEKIND_BOOLEAN) + return false; + UA_STACKARRAY(char, buf, type->memSize); + memset(buf, 0, type->memSize); + return (UA_order(buf, p, type) == UA_ORDER_EQ); +} + static status UA_FUNC_ATTR_WARN_UNUSED_RESULT xmlEncodeWriteChars(CtxXml *ctx, const char *c, size_t len) { if(ctx->pos + len > ctx->end) @@ -533,6 +589,62 @@ ENCODE_XML(ExtensionObject) { return ret; } +static status +Array_encodeXml(CtxXml *ctx, const void *ptr, size_t length, + const UA_DataType *type) { + size_t arrNameLen = strlen("ListOf") + strlen(type->typeName); + char* arrName = (char*)UA_malloc((arrNameLen + 1) * sizeof(char)); + arrName[0] = 'L'; + arrName[1] = 'i'; + arrName[2] = 's'; + arrName[3] = 't'; + arrName[4] = 'O'; + arrName[5] = 'f'; + for(size_t i = 0; i < strlen(type->typeName); ++i) + arrName[strlen("ListOf") + i] = type->typeName[i]; + arrName[arrNameLen] = '\0'; + + status ret = writeXmlElemNameBegin(ctx, arrName); + + if(!ptr) + goto finish; + + uintptr_t uptr = (uintptr_t)ptr; + for(size_t i = 0; i < length && ret == UA_STATUSCODE_GOOD; ++i) { + if(isNullXml((const void*)uptr, type)) + ret |= xmlEncodeWriteChars(ctx, "null", 4); + else + ret |= writeXmlElement(ctx, type->typeName, (const void*)uptr, type); + uptr += type->memSize; + } + +finish: + ret |= writeXmlElemNameEnd(ctx, arrName, &UA_TYPES[UA_TYPES_VARIANT]); + UA_free(arrName); + return ret; +} + +ENCODE_XML(Variant) { + + if(!src->type) + return UA_STATUSCODE_BADENCODINGERROR; + + /* Set the content type in the encoding mask */ + const UA_Boolean isBuiltin = (src->type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO); + + /* Set the array type in the encoding mask */ + const bool isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL; + + if((!isArray && !isBuiltin) || src->arrayDimensionsSize > 1) + return UA_STATUSCODE_BADNOTIMPLEMENTED; + + status ret = writeXmlElemNameBegin(ctx, UA_XML_VARIANT_VALUE); + + ret |= Array_encodeXml(ctx, src->data, src->arrayLength, src->type); + + return ret | writeXmlElemNameEnd(ctx, UA_XML_VARIANT_VALUE, &UA_TYPES[UA_TYPES_VARIANT]); +} + static status encodeXmlNotImplemented(CtxXml *ctx, const void *src, const UA_DataType *type) { (void)ctx, (void)src, (void)type; @@ -563,7 +675,7 @@ const encodeXmlSignature encodeXmlJumpTable[UA_DATATYPEKINDS] = { (encodeXmlSignature)LocalizedText_encodeXml, /* LocalizedText */ (encodeXmlSignature)ExtensionObject_encodeXml, /* ExtensionObject */ (encodeXmlSignature)encodeXmlNotImplemented, /* DataValue */ - (encodeXmlSignature)encodeXmlNotImplemented, /* Variant */ + (encodeXmlSignature)Variant_encodeXml, /* Variant */ (encodeXmlSignature)encodeXmlNotImplemented, /* DiagnosticInfo */ (encodeXmlSignature)encodeXmlNotImplemented, /* Decimal */ (encodeXmlSignature)encodeXmlNotImplemented, /* Enum */ @@ -1471,8 +1583,36 @@ DECODE_XML(ExtensionObject) { static status Array_decodeXml(ParseCtxXml *ctx, void **dst, const UA_DataType *type) { - (void)dst, (void)type, (void)ctx; - return UA_STATUSCODE_BADNOTIMPLEMENTED; + if(strncmp(ctx->dataMembers[ctx->index]->name, "ListOf", strlen("ListOf"))) + return UA_STATUSCODE_BADDECODINGERROR; + + if(ctx->dataMembers[ctx->index]->type == XML_DATA_TYPE_PRIMITIVE) { + /* Return early for empty arrays */ + *dst = UA_EMPTY_ARRAY_SENTINEL; + return UA_STATUSCODE_GOOD; + } + + size_t length = ctx->dataMembers[ctx->index]->value.complex.membersSize; + ctx->index++; /* Go to first array member. */ + + /* Allocate memory */ + *dst = UA_calloc(length, type->memSize); + if(*dst == NULL) + return UA_STATUSCODE_BADOUTOFMEMORY; + + /* Decode array members */ + uintptr_t ptr = (uintptr_t)*dst; + for(size_t i = 0; i < length; ++i) { + status ret = decodeXmlJumpTable[type->typeKind](ctx, (void*)ptr, type); + if(ret != UA_STATUSCODE_GOOD) { + UA_Array_delete(*dst, i + 1, type); + *dst = NULL; + return ret; + } + ptr += type->memSize; + } + + return UA_STATUSCODE_GOOD; } static status @@ -1545,17 +1685,19 @@ addNewXmlMember(XmlData *parent, const char *name) { static void deleteData(XmlData *data) { - if(data->type == XML_DATA_TYPE_PRIMITIVE) { - if(data->value.primitive.value != NULL) - UA_free((void*)(uintptr_t)data->value.primitive.value); + if(data != NULL) { + if(data->type == XML_DATA_TYPE_PRIMITIVE) { + if(data->value.primitive.value != NULL) + UA_free((void*)(uintptr_t)data->value.primitive.value); + } + else { + for(size_t cnt = 0LU; cnt < data->value.complex.membersSize; ++cnt) + deleteData(data->value.complex.members[cnt]); + UA_free(data->value.complex.members); + } + UA_free((void*)(uintptr_t)data->name); + UA_free(data); } - else { - for(size_t cnt = 0LU; cnt < data->value.complex.membersSize; ++cnt) - deleteData(data->value.complex.members[cnt]); - UA_free(data->value.complex.members); - } - UA_free((void*)(uintptr_t)data->name); - UA_free(data); } static void @@ -1669,7 +1811,6 @@ UA_decodeXml(const UA_ByteString *src, void *dst, const UA_DataType *type, memset(&value, 0, sizeof(XmlValue)); memset(&parseCtx, 0, sizeof(XmlParsingCtx)); memset(&ctx, 0, sizeof(ParseCtxXml)); - memset(dst, 0, type->memSize); ctx.dataMembers = (XmlData**)UA_malloc(UA_XML_MAXMEMBERSCOUNT * sizeof(XmlData*)); ctx.parseCtx = &parseCtx; @@ -1689,12 +1830,12 @@ UA_decodeXml(const UA_ByteString *src, void *dst, const UA_DataType *type, } /* Decode */ - memset(dst, 0, type->memSize); /* Initialize the value */ - if(ctx.value->isArray) ret = Array_decodeXml(&ctx, (void**)dst, type); - else + else { + memset(dst, 0, type->memSize); /* Initialize the value */ ret = decodeXmlJumpTable[type->typeKind](&ctx, dst, type); + } if(ret != UA_STATUSCODE_GOOD) { deleteData(ctx.value->data); diff --git a/tests/check_types_builtin_xml.c b/tests/check_types_builtin_xml.c index 60b8ff32b..19e29a3a4 100644 --- a/tests/check_types_builtin_xml.c +++ b/tests/check_types_builtin_xml.c @@ -1903,6 +1903,435 @@ START_TEST(UA_ExtensionObject_print_xml_encode) { UA_ExtensionObject_delete(src); } +/* Array */ +START_TEST(UA_Array_Variant_Byte_xml_encode) { + UA_Variant *src = UA_Variant_new(); + UA_Variant_init(src); + UA_Byte zero[2] = {42, 43}; + UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_BYTE]); + + const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT]; + const size_t variantXmlSectLen = 34; + size_t size = UA_calcSizeXml((void*)src, type, NULL); + ck_assert_uint_eq(size, + xmlEncTypeDefs[type->typeKind].xmlEncTypeDefLen + variantXmlSectLen + 55); + + UA_ByteString buf; + UA_ByteString_allocBuffer(&buf, size + 1); + + status s = UA_encodeXml((void*)src, type, &buf, NULL); + ck_assert_int_eq(s, UA_STATUSCODE_GOOD); + + char result[size + 1]; + sprintf(result, "%s" + "" + "" + "" + "42" + "43" + "" + "" + "", + xmlEncTypeDefs[type->typeKind].xmlEncTypeDef); + buf.data[size] = 0; /* zero terminate */ + ck_assert_str_eq(result, (char*)buf.data); + + UA_ByteString_clear(&buf); + UA_Variant_delete(src); +} +END_TEST + +START_TEST(UA_Array_Variant_UInt16_xml_encode) { + UA_Variant *src = UA_Variant_new(); + UA_Variant_init(src); + UA_UInt16 zero[2] = {42, 43}; + UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_UINT16]); + + const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT]; + const size_t variantXmlSectLen = 34; + size_t size = UA_calcSizeXml((void*)src, type, NULL); + ck_assert_uint_eq(size, + xmlEncTypeDefs[type->typeKind].xmlEncTypeDefLen + variantXmlSectLen + 67); + + UA_ByteString buf; + UA_ByteString_allocBuffer(&buf, size + 1); + + status s = UA_encodeXml((void*)src, type, &buf, NULL); + ck_assert_int_eq(s, UA_STATUSCODE_GOOD); + + char result[size + 1]; + sprintf(result, "%s" + "" + "" + "" + "42" + "43" + "" + "" + "", + xmlEncTypeDefs[type->typeKind].xmlEncTypeDef); + buf.data[size] = 0; /* zero terminate */ + ck_assert_str_eq(result, (char*)buf.data); + + UA_ByteString_clear(&buf); + UA_Variant_delete(src); +} +END_TEST + +START_TEST(UA_Array_Variant_Null_xml_encode) { + UA_Variant *src = UA_Variant_new(); + UA_Variant_init(src); + UA_Variant_setArray(src, NULL, 0, &UA_TYPES[UA_TYPES_UINT16]); + + const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT]; + size_t size = UA_calcSizeXml((void*)src, type, NULL); + + UA_ByteString buf; + UA_ByteString_allocBuffer(&buf, size + 1); + + status s = UA_encodeXml((void*)src, type, &buf, NULL); + ck_assert_int_eq(s, UA_STATUSCODE_GOOD); + + char result[size + 1]; + sprintf(result, "%s" + "" + "" + "" + "" + "" + "", + xmlEncTypeDefs[type->typeKind].xmlEncTypeDef); + buf.data[size] = 0; /* zero terminate */ + ck_assert_str_eq(result, (char*)buf.data); + + UA_ByteString_clear(&buf); + UA_Variant_delete(src); +} +END_TEST + +START_TEST(UA_Array_Variant_String_xml_encode) { + UA_Variant *src = UA_Variant_new(); + UA_Variant_init(src); + UA_String zero[2] = {UA_STRING("eins"),UA_STRING("zwei")}; + UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_STRING]); + + const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT]; + size_t size = UA_calcSizeXml((void*)src, type, NULL); + + UA_ByteString buf; + UA_ByteString_allocBuffer(&buf, size + 1); + + status s = UA_encodeXml((void*)src, type, &buf, NULL); + ck_assert_int_eq(s, UA_STATUSCODE_GOOD); + + char result[size + 1]; + sprintf(result, "%s" + "" + "" + "" + "eins" + "zwei" + "" + "" + "", + xmlEncTypeDefs[type->typeKind].xmlEncTypeDef); + buf.data[size] = 0; /* zero terminate */ + ck_assert_str_eq(result, (char*)buf.data); + UA_ByteString_clear(&buf); + UA_Variant_delete(src); +} +END_TEST + +START_TEST(UA_Array_Variant_ExtensionObject_ByteString_xml_encode) { + UA_ExtensionObject eo1; + UA_ExtensionObject_init(&eo1); + eo1.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; + eo1.content.encoded.typeId = UA_NODEID_NUMERIC(2, 1234); + eo1.content.encoded.body = UA_BYTESTRING_ALLOC("123456789012345678901234567890"); + + UA_ExtensionObject eo2; + UA_ExtensionObject_init(&eo2); + eo2.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; + eo2.content.encoded.typeId = UA_NODEID_NUMERIC(3, 5678); + eo2.content.encoded.body = UA_BYTESTRING_ALLOC("98765432109876543210987654321"); + + UA_ExtensionObject eo3; + UA_ExtensionObject_init(&eo3); + eo3.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; + eo3.content.encoded.typeId = UA_NODEID_NUMERIC(4, 9999); + eo3.content.encoded.body = UA_BYTESTRING_ALLOC("1357911131517192123252729"); + + UA_Variant *src = UA_Variant_new(); + UA_Variant_init(src); + UA_ExtensionObject zero[3] = {eo1, eo2, eo3}; + UA_Variant_setArrayCopy(src, zero, 3, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); + + const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT]; + size_t size = UA_calcSizeXml((void*)src, type, NULL); + + UA_ByteString buf; + UA_ByteString_allocBuffer(&buf, size + 1); + + status s = UA_encodeXml((void*)src, type, &buf, NULL); + ck_assert_int_eq(s, UA_STATUSCODE_GOOD); + + char result[size + 1]; + sprintf(result, "%s" + "" + "" + "" + "" + "" + "ns=2;i=1234" + "" + "" + "123456789012345678901234567890" + "" + "" + "" + "" + "ns=3;i=5678" + "" + "" + "98765432109876543210987654321" + "" + "" + "" + "" + "ns=4;i=9999" + "" + "" + "1357911131517192123252729" + "" + "" + "" + "" + "", + xmlEncTypeDefs[type->typeKind].xmlEncTypeDef); + buf.data[size] = 0; /* zero terminate */ + ck_assert_str_eq(result, (char*)buf.data); + + UA_ByteString_clear(&buf); + UA_ExtensionObject_clear(&eo1); + UA_ExtensionObject_clear(&eo2); + UA_ExtensionObject_clear(&eo3); + UA_Variant_delete(src); +} +END_TEST + +START_TEST(UA_Array_Variant_ExtensionObject_Xml_xml_encode) { + UA_ExtensionObject eo1; + UA_ExtensionObject_init(&eo1); + eo1.encoding = UA_EXTENSIONOBJECT_ENCODED_XML; + eo1.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297); + eo1.content.encoded.body = UA_BYTESTRING_ALLOC("" + "ObjectToMoveOrCopy" + "" + "i=17" + "" + "-1" + "" + ""); + + UA_ExtensionObject eo2; + UA_ExtensionObject_init(&eo2); + eo2.encoding = UA_EXTENSIONOBJECT_ENCODED_XML; + eo2.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297); + eo2.content.encoded.body = UA_BYTESTRING_ALLOC("" + "TargetDirectory" + "" + "i=17" + "" + "-1" + "" + ""); + + UA_ExtensionObject eo3; + UA_ExtensionObject_init(&eo3); + eo3.encoding = UA_EXTENSIONOBJECT_ENCODED_XML; + eo3.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297); + eo3.content.encoded.body = UA_BYTESTRING_ALLOC("" + "CreateCopy" + "" + "i=1" + "" + "-1" + "" + ""); + + UA_ExtensionObject eo4; + UA_ExtensionObject_init(&eo4); + eo4.encoding = UA_EXTENSIONOBJECT_ENCODED_XML; + eo4.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297); + eo4.content.encoded.body = UA_BYTESTRING_ALLOC("" + "NewName" + "" + "i=12" + "" + "-1" + "" + ""); + + UA_Variant *src = UA_Variant_new(); + UA_Variant_init(src); + UA_ExtensionObject zero[4] = {eo1, eo2, eo3, eo4}; + UA_Variant_setArrayCopy(src, zero, 4, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); + + const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT]; + size_t size = UA_calcSizeXml((void*)src, type, NULL); + + UA_ByteString buf; + UA_ByteString_allocBuffer(&buf, size + 1); + + status s = UA_encodeXml((void*)src, type, &buf, NULL); + ck_assert_int_eq(s, UA_STATUSCODE_GOOD); + + char result[size + 1]; + sprintf(result, "%s" + "" + "" + "" + "" + "" + "i=297" + "" + "" + "" + "ObjectToMoveOrCopy" + "" + "i=17" + "" + "-1" + "" + "" + "" + "" + "" + "" + "i=297" + "" + "" + "" + "TargetDirectory" + "" + "i=17" + "" + "-1" + "" + "" + "" + "" + "" + "" + "i=297" + "" + "" + "" + "CreateCopy" + "" + "i=1" + "" + "-1" + "" + "" + "" + "" + "" + "" + "i=297" + "" + "" + "" + "NewName" + "" + "i=12" + "" + "-1" + "" + "" + "" + "" + "" + "" + "", + xmlEncTypeDefs[type->typeKind].xmlEncTypeDef); + buf.data[size] = 0; /* zero terminate */ + ck_assert_str_eq(result, (char*)buf.data); + + UA_ByteString_clear(&buf); + UA_ExtensionObject_clear(&eo1); + UA_ExtensionObject_clear(&eo2); + UA_ExtensionObject_clear(&eo3); + UA_ExtensionObject_clear(&eo4); + UA_Variant_delete(src); +} +END_TEST + +START_TEST(UA_Array_Variant_ExtensionObject_print_xml_encode) { + UA_ExtensionObject eo1; + UA_ExtensionObject_init(&eo1); + eo1.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; + eo1.content.encoded.typeId = UA_NODEID_NUMERIC(2, 1234); + eo1.content.encoded.body = UA_BYTESTRING_ALLOC("123456789012345678901234567890"); + + UA_ExtensionObject eo2; + UA_ExtensionObject_init(&eo2); + eo2.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; + eo2.content.encoded.typeId = UA_NODEID_NUMERIC(3, 5678); + eo2.content.encoded.body = UA_BYTESTRING_ALLOC("98765432109876543210987654321"); + + UA_Variant *src = UA_Variant_new(); + UA_Variant_init(src); + UA_ExtensionObject zero[2] = {eo1, eo2}; + UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); + + UA_EncodeXmlOptions options; + memset(&options, 0, sizeof(UA_EncodeXmlOptions)); + options.prettyPrint = true; + const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT]; + size_t size = UA_calcSizeXml((void*)src, type, &options); + + UA_ByteString buf; + UA_ByteString_allocBuffer(&buf, size + 1); + + status s = UA_printXml((void*)src, type, &buf); + ck_assert_int_eq(s, UA_STATUSCODE_GOOD); + + char result[size + 1]; + sprintf(result, "%s" + "\n" + "\n\t" + "\n\t\t" + "\n\t\t\t" + "\n\t\t\t\t" + "\n\t\t\t\t\tns=2;i=1234" + "\n\t\t\t\t" + "\n\t\t\t\t" + "\n\t\t\t\t\t123456789012345678901234567890" + "\n\t\t\t\t" + "\n\t\t\t" + "\n\t\t\t" + "\n\t\t\t\t" + "\n\t\t\t\t\tns=3;i=5678" + "\n\t\t\t\t" + "\n\t\t\t\t" + "\n\t\t\t\t\t98765432109876543210987654321" + "\n\t\t\t\t" + "\n\t\t\t" + "\n\t\t" + "\n\t" + "\n", + xmlEncTypeDefs[type->typeKind].xmlEncTypeDef); + buf.data[size] = 0; /* zero terminate */ + ck_assert_str_eq(result, (char*)buf.data); + + UA_ByteString_clear(&buf); + UA_ExtensionObject_clear(&eo1); + UA_ExtensionObject_clear(&eo2); + UA_Variant_delete(src); +} +END_TEST + // ---------------------------DECODE------------------------------------- @@ -3708,6 +4137,247 @@ START_TEST(UA_ExtensionObject_InvalidBody_xml_decode) { } END_TEST +/* Array */ +START_TEST(UA_Array_Boolean_xml_decode) { + UA_Boolean *out; + UA_ByteString buf = UA_STRING("" + "true" + "false" + ""); + + UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_BOOLEAN], NULL); + + ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); + ck_assert_int_eq(out[0], true); + ck_assert_int_eq(out[1], false); + + UA_Array_delete(out, 2, &UA_TYPES[UA_TYPES_BOOLEAN]); +} +END_TEST + +START_TEST(UA_Array_Number_xml_decode) { + UA_UInt32 *out; + UA_ByteString buf = UA_STRING("" + "0" + "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + ""); + + UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL); + + ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); + for(size_t i = 0; i < 10; ++i) + ck_assert_int_eq(out[i], (UA_UInt32)i); + + UA_Array_delete(out, 10, &UA_TYPES[UA_TYPES_UINT32]); +} +END_TEST + +START_TEST(UA_Array_Null_xml_decode) { + UA_UInt32 *out; + UA_ByteString buf = UA_STRING("" + ""); + + UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL); + + ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); + ck_assert_ptr_eq(out, (UA_UInt32*)UA_EMPTY_ARRAY_SENTINEL); +} +END_TEST + +START_TEST(UA_Array_String_xml_decode) { + UA_String *out; + UA_ByteString buf = UA_STRING("" + "eins" + "zwei" + "drei" + "vier" + ""); + + UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_STRING], NULL); + + ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); + UA_String testData1 = UA_STRING("eins"); + UA_String testData2 = UA_STRING("zwei"); + UA_String testData3 = UA_STRING("drei"); + UA_String testData4 = UA_STRING("vier"); + ck_assert(UA_String_equal(&out[0], &testData1)); + ck_assert(UA_String_equal(&out[1], &testData2)); + ck_assert(UA_String_equal(&out[2], &testData3)); + ck_assert(UA_String_equal(&out[3], &testData4)); + + UA_Array_delete(out, 4, &UA_TYPES[UA_TYPES_STRING]); +} +END_TEST + +START_TEST(UA_Array_ExtensionObject_ByteString_xml_decode) { + UA_ExtensionObject *out; + UA_ByteString buf = UA_STRING("" + "" + "" + "ns=2;i=1234" + "" + "" + "123456789012345678901234567890" + "" + "" + "" + "" + "ns=3;i=5678" + "" + "" + "98765432109876543210987654321" + "" + "" + "" + "" + "ns=4;i=9999" + "" + "" + "1357911131517192123252729" + "" + "" + ""); + + UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], NULL); + + ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); + + ck_assert_int_eq(out[0].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING); + UA_String testData1 = UA_STRING("123456789012345678901234567890"); + ck_assert(UA_String_equal(&out[0].content.encoded.body, &testData1)); + ck_assert_int_eq(out[0].content.encoded.typeId.namespaceIndex, 2); + ck_assert_int_eq(out[0].content.encoded.typeId.identifier.numeric, 1234); + ck_assert_int_eq(out[1].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING); + UA_String testData2 = UA_STRING("98765432109876543210987654321"); + ck_assert(UA_String_equal(&out[1].content.encoded.body, &testData2)); + ck_assert_int_eq(out[1].content.encoded.typeId.namespaceIndex, 3); + ck_assert_int_eq(out[1].content.encoded.typeId.identifier.numeric, 5678); + ck_assert_int_eq(out[2].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING); + UA_String testData3 = UA_STRING("1357911131517192123252729"); + ck_assert(UA_String_equal(&out[2].content.encoded.body, &testData3)); + ck_assert_int_eq(out[2].content.encoded.typeId.namespaceIndex, 4); + ck_assert_int_eq(out[2].content.encoded.typeId.identifier.numeric, 9999); + + UA_Array_delete(out, 3, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); +} +END_TEST + +START_TEST(UA_Array_ExtensionObject_Xml_ByteString_xml_decode) { + UA_ExtensionObject *out; + UA_ByteString buf = UA_STRING("" + "" + "" + "i=297" + "" + "" + "" + "ObjectToMoveOrCopy" + "" + "i=17" + "" + "-1" + "" + "" + "" + "" + "" + "" + "i=297" + "" + "" + "" + "TargetDirectory" + "" + "i=17" + "" + "-1" + "" + "" + "" + "" + "" + "" + "i=297" + "" + "" + "" + "CreateCopy" + "" + "i=1" + "" + "-1" + "" + "" + "" + "" + "" + "" + "ns=4;i=9999" + "" + "" + "1357911131517192123252729" + "" + "" + ""); + + UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], NULL); + + ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); + + ck_assert_int_eq(out[0].encoding, UA_EXTENSIONOBJECT_ENCODED_XML); + UA_String testData1 = UA_STRING("" + "ObjectToMoveOrCopy" + "" + "i=17" + "" + "-1" + "" + ""); + ck_assert(UA_String_equal(&out[0].content.encoded.body, &testData1)); + ck_assert_int_eq(out[0].content.encoded.typeId.namespaceIndex, 0); + ck_assert_int_eq(out[0].content.encoded.typeId.identifier.numeric, 297); + ck_assert_int_eq(out[1].encoding, UA_EXTENSIONOBJECT_ENCODED_XML); + UA_String testData2 = UA_STRING("" + "TargetDirectory" + "" + "i=17" + "" + "-1" + "" + ""); + ck_assert(UA_String_equal(&out[1].content.encoded.body, &testData2)); + ck_assert_int_eq(out[1].content.encoded.typeId.namespaceIndex, 0); + ck_assert_int_eq(out[1].content.encoded.typeId.identifier.numeric, 297); + ck_assert_int_eq(out[2].encoding, UA_EXTENSIONOBJECT_ENCODED_XML); + UA_String testData3 = UA_STRING("" + "CreateCopy" + "" + "i=1" + "" + "-1" + "" + ""); + ck_assert(UA_String_equal(&out[2].content.encoded.body, &testData3)); + ck_assert_int_eq(out[2].content.encoded.typeId.namespaceIndex, 0); + ck_assert_int_eq(out[2].content.encoded.typeId.identifier.numeric, 297); + ck_assert_int_eq(out[3].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING); + UA_String testData4 = UA_STRING("1357911131517192123252729"); + ck_assert(UA_String_equal(&out[3].content.encoded.body, &testData4)); + ck_assert_int_eq(out[3].content.encoded.typeId.namespaceIndex, 4); + ck_assert_int_eq(out[3].content.encoded.typeId.identifier.numeric, 9999); + + UA_Array_delete(out, 4, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]); +} +END_TEST + static Suite *testSuite_builtin_xml(void) { Suite *s = suite_create("Built-in Data Types 62541-5 Xml"); @@ -3814,6 +4484,14 @@ static Suite *testSuite_builtin_xml(void) { tcase_add_test(tc_xml_encode, UA_ExtensionObject_null_xml_encode); tcase_add_test(tc_xml_encode, UA_ExtensionObject_print_xml_encode); + tcase_add_test(tc_xml_encode, UA_Array_Variant_Byte_xml_encode); + tcase_add_test(tc_xml_encode, UA_Array_Variant_UInt16_xml_encode); + tcase_add_test(tc_xml_encode, UA_Array_Variant_Null_xml_encode); + tcase_add_test(tc_xml_encode, UA_Array_Variant_String_xml_encode); + tcase_add_test(tc_xml_encode, UA_Array_Variant_ExtensionObject_ByteString_xml_encode); + tcase_add_test(tc_xml_encode, UA_Array_Variant_ExtensionObject_Xml_xml_encode); + tcase_add_test(tc_xml_encode, UA_Array_Variant_ExtensionObject_print_xml_encode); + suite_add_tcase(s, tc_xml_encode); TCase *tc_xml_decode = tcase_create("xml_decode"); @@ -3922,6 +4600,13 @@ static Suite *testSuite_builtin_xml(void) { tcase_add_test(tc_xml_decode, UA_ExtensionObject_EncodedXml_4_xml_decode); tcase_add_test(tc_xml_decode, UA_ExtensionObject_InvalidBody_xml_decode); + tcase_add_test(tc_xml_decode, UA_Array_Boolean_xml_decode); + tcase_add_test(tc_xml_decode, UA_Array_Number_xml_decode); + tcase_add_test(tc_xml_decode, UA_Array_Null_xml_decode); + tcase_add_test(tc_xml_decode, UA_Array_String_xml_decode); + tcase_add_test(tc_xml_decode, UA_Array_ExtensionObject_ByteString_xml_decode); + tcase_add_test(tc_xml_decode, UA_Array_ExtensionObject_Xml_ByteString_xml_decode); + suite_add_tcase(s, tc_xml_decode); return s;