diff options
Diffstat (limited to 'tests/utests/data')
-rw-r--r-- | tests/utests/data/test_diff.c | 258 | ||||
-rw-r--r-- | tests/utests/data/test_new.c | 63 | ||||
-rw-r--r-- | tests/utests/data/test_parser_json.c | 172 | ||||
-rw-r--r-- | tests/utests/data/test_parser_xml.c | 226 | ||||
-rw-r--r-- | tests/utests/data/test_printer_xml.c | 3 | ||||
-rw-r--r-- | tests/utests/data/test_tree_data.c | 35 | ||||
-rw-r--r-- | tests/utests/data/test_validation.c | 93 |
7 files changed, 804 insertions, 46 deletions
diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c index 1b7592a..4400b5d 100644 --- a/tests/utests/data/test_diff.c +++ b/tests/utests/data/test_diff.c @@ -4,7 +4,7 @@ * @author Michal Vasko <mvasko@cesnet.cz> * @brief tests for lyd_diff() * - * Copyright (c) 2020 CESNET, z.s.p.o. + * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -17,15 +17,15 @@ #include "libyang.h" -#define CHECK_PARSE_LYD(INPUT, MODEL) \ - CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, MODEL) +#define CHECK_PARSE_LYD(INPUT, OUTPUT) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, OUTPUT) -#define CHECK_LYD_STRING(IN_MODEL, TEXT) \ - CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS) +#define CHECK_LYD_STRING(INPUT, TEXT) \ + CHECK_LYD_STRING_PARAM(INPUT, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS) -#define CHECK_PARSE_LYD_DIFF(INPUT_1, INPUT_2, OUT_MODEL) \ - assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, 0, &OUT_MODEL));\ - assert_non_null(OUT_MODEL) +#define CHECK_PARSE_LYD_DIFF(INPUT_1, INPUT_2, OUT_DIFF) \ + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, 0, &OUT_DIFF));\ + assert_non_null(OUT_DIFF) #define TEST_DIFF_3(XML1, XML2, XML3, DIFF1, DIFF2, MERGE) \ { \ @@ -117,6 +117,12 @@ const char *schema1 = " leaf l2 {\n" " type int32;\n" " }\n" + "" + " container cont {\n" + " leaf l3 {\n" + " type string;\n" + " }\n" + " }\n" " }\n" "" " leaf-list dllist {\n" @@ -312,6 +318,8 @@ test_invalid(void **state) struct lyd_node *diff = NULL; assert_int_equal(lyd_diff_siblings(model_1, lyd_child(model_1), 0, &diff), LY_EINVAL); + CHECK_LOG_CTX("Invalid arguments - cannot create diff for unrelated data (lyd_diff()).", NULL); + assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, NULL), LY_EINVAL); lyd_free_all(model_1); @@ -637,6 +645,126 @@ test_list(void **state) } static void +test_nested_list(void **state) +{ + struct lyd_node *data1, *data2, *diff; + const char *xml1, *xml2; + + (void) state; + + xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">" + " <list>" + " <name>n1</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n2</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n3</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n4</name>" + " <value>25</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + " <list>" + " <name>n0</name>" + " <value>26</value>" + " <list2>" + " <name2>n22</name2>" + " <value2>26</value2>" + " </list2>" + " <list2>" + " <name2>n23</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + "</df>"; + xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">" + " <list>" + " <name>n0</name>" + " <value>30</value>" + " <list2>" + " <name2>n23</name2>" + " <value2>26</value2>" + " </list2>" + " </list>" + "</df>"; + + CHECK_PARSE_LYD(xml1, data1); + CHECK_PARSE_LYD(xml2, data2); + CHECK_PARSE_LYD_DIFF(data1, data2, diff); + + CHECK_LYD_STRING(diff, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list yang:operation=\"delete\">\n" + " <name>n1</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"delete\">\n" + " <name>n2</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"delete\">\n" + " <name>n3</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"delete\">\n" + " <name>n4</name>\n" + " <value>25</value>\n" + " <list2>\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + " <list yang:operation=\"none\">\n" + " <name>n0</name>\n" + " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"26\">30</value>\n" + " <list2 yang:operation=\"delete\">\n" + " <name2>n22</name2>\n" + " <value2>26</value2>\n" + " </list2>\n" + " </list>\n" + "</df>\n"); + + lyd_free_all(data1); + lyd_free_all(data2); + lyd_free_all(diff); +} + +static void test_userord_llist(void **state) { (void) state; @@ -940,6 +1068,118 @@ test_userord_list2(void **state) } static void +test_userord_list3(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <cont>\n" + " <l3>val1</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>4</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " <cont>\n" + " <l3>val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>44</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " <cont>\n" + " <l3>val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>44</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </ul>\n" + "</df>\n"; + + const char *out_diff_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"create\">3</l2>\n" + " <cont yang:operation=\"none\">\n" + " <l3 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"val1\">val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n" + " <l1>d</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_diff_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='c']\">\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"delete\">1</l2>\n" + " </ul>\n" + "</df>\n"; + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"create\">3</l2>\n" + " <cont yang:operation=\"none\">\n" + " <l3 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"val1\">val2</l3>\n" + " </cont>\n" + " </ul>\n" + " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n" + " <l1>d</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n" + " </ul>\n" + " <ul yang:key=\"\" yang:orig-key=\"[l1='c']\" yang:operation=\"replace\">\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"delete\">1</l2>\n" + " </ul>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void test_keyless_list(void **state) { (void) state; @@ -1207,11 +1447,13 @@ main(void) UTEST(test_delete_merge, setup), UTEST(test_leaf, setup), UTEST(test_list, setup), + UTEST(test_nested_list, setup), UTEST(test_userord_llist, setup), UTEST(test_userord_llist2, setup), UTEST(test_userord_mix, setup), UTEST(test_userord_list, setup), UTEST(test_userord_list2, setup), + UTEST(test_userord_list3, setup), UTEST(test_keyless_list, setup), UTEST(test_state_llist, setup), UTEST(test_wd, setup), diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c index 7642960..5cee903 100644 --- a/tests/utests/data/test_new.c +++ b/tests/utests/data/test_new.c @@ -55,6 +55,7 @@ const char *schema_a = "module a {\n" " anydata any {\n" " config false;\n" " }\n" + " anyxml anyx;\n" " leaf-list ll2 {\n" " config false;\n" " type string;\n" @@ -128,6 +129,16 @@ test_top_level(void **state) assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a= 'a']\n[b =\t'b']", 0, &node), LY_SUCCESS); lyd_free_tree(node); + const char *key_vals[] = {"a", "b"}; + + assert_int_equal(lyd_new_list3(NULL, mod, "l1", key_vals, NULL, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + uint32_t val_lens[] = {1, 1}; + + assert_int_equal(lyd_new_list3_bin(NULL, mod, "l1", (const void **)key_vals, val_lens, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + /* leaf */ assert_int_equal(lyd_new_term(NULL, mod, "foo", "[a='a'][b='b'][c='c']", 0, &node), LY_EVALID); CHECK_LOG_CTX("Invalid type uint16 value \"[a='a'][b='b'][c='c']\".", "Schema location \"/a:foo\"."); @@ -290,6 +301,7 @@ test_path(void **state) ret = lyd_new_path2(root, NULL, "/a:c2/l3[1]", NULL, 0, 0, 0, NULL, &node); assert_int_equal(ret, LY_EEXIST); + CHECK_LOG_CTX("Path \"/a:c2/l3[1]\" already exists.", "Data location \"/a:c2/l3[1]\"."); ret = lyd_new_path2(root, NULL, "/a:c2/l3[2]/x", "val2", 0, 0, 0, NULL, &node); assert_int_equal(ret, LY_SUCCESS); @@ -348,6 +360,7 @@ test_path(void **state) ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "", 0, 0, 0, NULL, &node); assert_int_equal(ret, LY_EEXIST); + CHECK_LOG_CTX("Path \"/a:ll2[1]\" already exists.", "Data location \"/a:ll2[1]\"."); ret = lyd_new_path2(root, NULL, "/a:ll2[2]", "val2", 0, 0, 0, NULL, &node); assert_int_equal(ret, LY_SUCCESS); @@ -362,6 +375,7 @@ test_path(void **state) ret = lyd_new_path2(root, NULL, "/a:ll2[3][.='val3']", NULL, 0, 0, 0, NULL, &node); assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Unparsed characters \"[.='val3']\" left at the end of path.", NULL); lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); assert_string_equal(str, @@ -391,6 +405,55 @@ test_path(void **state) "}\n"); free(str); lyd_free_siblings(root); + + /* anyxml */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "<a/><b/><c/>", 0, LYD_ANYDATA_XML, 0, &root, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<anyx xmlns=\"urn:tests:a\">\n" + " <a/>\n" + " <b/>\n" + " <c/>\n" + "</anyx>\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:anyx\": {\n" + " \"a\": [null],\n" + " \"b\": [null],\n" + " \"c\": [null]\n" + " }\n" + "}\n"); + free(str); + lyd_free_siblings(root); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "{\"a\":[null],\"b\":[null],\"c\":[null]}", 0, LYD_ANYDATA_JSON, 0, &root, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<anyx xmlns=\"urn:tests:a\">\n" + " <a/>\n" + " <b/>\n" + " <c/>\n" + "</anyx>\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:anyx\": {\n" + " \"a\": [null],\n" + " \"b\": [null],\n" + " \"c\": [null]\n" + " }\n" + "}\n"); + free(str); + lyd_free_siblings(root); } static void diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c index d341e31..8feed9c 100644 --- a/tests/utests/data/test_parser_json.c +++ b/tests/utests/data/test_parser_json.c @@ -1,9 +1,10 @@ -/* +/** * @file test_parser_json.c - * @author: Radek Krejci <rkrejci@cesnet.cz> - * @brief unit tests for functions from parser_xml.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for JSON parser * - * Copyright (c) 2019 CESNET, z.s.p.o. + * Copyright (c) 2019 - 2023 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -44,7 +45,10 @@ setup(void **state) "leaf-list ll1 { type uint8; }" "leaf foo2 { type string; default \"default-val\"; }" "leaf foo3 { type uint32; }" - "notification n2;}"; + "leaf foo4 { type uint64; }" + "rpc r1 {input {leaf l1 {type string;} leaf l2 {type string;}}}" + "notification n2;" + "}"; UTEST_SETUP; @@ -160,6 +164,7 @@ test_leaf(void **state) /* reverse solidus in JSON object member name */ data = "{\"@a:foo\":{\"a:hi\\nt\":1},\"a:foo\":\"xxx\"}"; assert_int_equal(LY_EINVAL, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LOG_CTX("Annotation definition for attribute \"a:hi\nt\" not found.", "Path \"/@a:foo/@a:hi\nt\", line number 1."); } static void @@ -427,12 +432,10 @@ test_list(void **state) assert_non_null(leaf = (struct lyd_node_term *)leaf->next); CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "d", 1, LYS_LEAF, 1, 0, NULL, 0); CHECK_LOG_CTX(NULL, NULL); - CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1,\"d\":\"d\"}]}"); lyd_free_all(tree); - /* */ CHECK_PARSE_LYD("{\"a:l1\":[{\"c\":1,\"b\":\"b\",\"a\":\"a\"}]}", LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree); CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1", 1, LYS_LIST, 0, 0, NULL, 0); @@ -447,11 +450,16 @@ test_list(void **state) CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0); CHECK_LOG_CTX(NULL, NULL); - CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1}]}"); lyd_free_all(tree); + /* skip unknown nested nodes */ + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":3,\"counters\":{\"count1\":\"c1\",\"count2\":\"c2\"}}]}"; + CHECK_PARSE_LYD(data, LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":3}]}"); + lyd_free_all(tree); + data = "{\"a:cp\":{\"@\":{\"a:hint\":1}}}"; CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); assert_non_null(tree); @@ -460,7 +468,6 @@ test_list(void **state) 1, LYS_CONTAINER, 0, 0, NULL, 0); CHECK_LYD_META(tree->meta, 1, "hint", 0, 1, INT8, "1", 1); assert_null(tree->meta->next); - CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); lyd_free_all(tree); } @@ -516,6 +523,25 @@ test_opaq(void **state) CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); lyd_free_all(tree); + /* special chars */ + data = "{\"a:foo3\":\"ab\\\"\\\\\\r\\t\"}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong encoding */ + data = "{\"a:foo3\":\"25\"}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo3", 0, 0, NULL, 0, "25"); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:foo4\":25}"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo4", 0, 0, NULL, 0, "25"); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + /* missing key, no flags */ data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"d\":\"val_d\"}]}"; PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, @@ -555,11 +581,12 @@ test_opaq(void **state) /* invalid metadata */ data = "{\"@a:foo\":\"str\",\"@a:foo3\":1,\"a:foo3\":2}"; PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, - "Unknown module of node \"@a:foo\".", "Data location \"/@a:foo\"."); + "Unknown module of node \"@a:foo\".", "Path \"/\"."); + CHECK_LOG_CTX("Missing JSON data instance to be coupled with @a:foo metadata.", "Data location \"/@a:foo\", line number 1."); /* empty name */ PARSER_CHECK_ERROR("{\"@a:foo\":{\"\":0}}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, - "A JSON object member name cannot be a zero-length string.", "Line number 1."); + "JSON object member name cannot be a zero-length string.", "Data location \"/@a:foo\", line number 1."); /* opaque data tree format print */ data = @@ -626,6 +653,7 @@ static void test_rpc(void **state) { const char *data; + char *str; struct ly_in *in; struct lyd_node *tree, *op; const struct lyd_node *node; @@ -675,8 +703,16 @@ test_rpc(void **state) CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); lyd_free_all(tree); - /* wrong namespace, element name, whatever... */ - /* TODO */ + /* append to parent */ + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:r1", NULL, 0, &op)); + assert_int_equal(LY_SUCCESS, ly_in_new_memory("{\"l1\": \"some str\", \"l2\": \"some other str\"}", &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_JSON, LYD_TYPE_RPC_YANG, &tree, NULL)); + ly_in_free(in, 0); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&str, op, LYD_JSON, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)); + lyd_free_tree(op); + assert_string_equal(str, "{\"a:r1\":{\"l1\":\"some str\",\"l2\":\"some other str\"}}"); + free(str); } static void @@ -772,6 +808,112 @@ test_reply(void **state) /* TODO */ } +static void +test_restconf_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL))); + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree)); + + data = "{\"ietf-netconf-nmda:input\":{" + "\"datastore\":\"ietf-datastores:running\"," + "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}}," + "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}" + "}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_JSON, LYD_TYPE_RPC_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* the same just connected to the edit-data RPC */ + data = "{\"ietf-netconf-nmda:edit-data\":{" + "\"datastore\":\"ietf-datastores:running\"," + "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}}," + "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}" + "}}"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void +test_restconf_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *ntf; + + data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"a:c\":{\"n1\":{\"nl\":\"value\"}}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf)); + ly_in_free(in, 0); + + /* envelopes separately */ + data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\"}}"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + /* notification with the parent node */ + data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}"; + CHECK_LYD_STRING(lyd_parent(ntf), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + lyd_free_all(tree); + lyd_free_all(ntf); + + /* wrong order */ + data = "{\"ietf-restconf:notification\":{\"a:n2\":{},\"eventTime\":\"2013-12-21T00:01:00Z\"}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf)); + ly_in_free(in, 0); + + lyd_free_all(tree); + lyd_free_all(ntf); + + /* unknown notification */ + data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"invalid:n2\":{}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf)); + UTEST_LOG_CTX_CLEAN; + ly_in_free(in, 0); + lyd_free_all(tree); +} + +static void +test_restconf_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree)); + + data = "{\"a:output\":{\"al\":25}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_JSON, LYD_TYPE_REPLY_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* connected to the RPC with the parent */ + data = "{\"a:c\":{\"act\":{\"al\":25}}}"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void +test_metadata(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid metadata value */ + data = "{\"a:c\":{\"x\":\"xval\",\"@x\":{\"a:hint\":\"value\"}}}"; + assert_int_equal(LY_EVALID, lyd_parse_data_mem(_UC->ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + CHECK_LOG_CTX("Invalid non-number-encoded int8 value \"value\".", "Path \"/a:c/x/@a:hint\", line number 1."); +} + int main(void) { @@ -787,6 +929,10 @@ main(void) UTEST(test_action, setup), UTEST(test_notification, setup), UTEST(test_reply, setup), + UTEST(test_restconf_rpc, setup), + UTEST(test_restconf_notification, setup), + UTEST(test_restconf_reply, setup), + UTEST(test_metadata, setup), }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c index 7defd9c..4f33f00 100644 --- a/tests/utests/data/test_parser_xml.c +++ b/tests/utests/data/test_parser_xml.c @@ -31,6 +31,7 @@ setup(void **state) " namespace urn:tests:a;\n" " prefix a;\n" " yang-version 1.1;\n" + " import ietf-yang-metadata {prefix md;}" " list l1 { key \"a b c\"; leaf a {type string;} leaf b {type string;} leaf c {type int16;}" " leaf d {type string;}" " container cont {leaf e {type boolean;}}" @@ -42,9 +43,12 @@ setup(void **state) " notification n1 { leaf nl {type string;}}}\n" " container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}\n" " anydata any {config false;}\n" + " anyxml anyx;\n" " leaf foo2 { type string; default \"default-val\"; }\n" " leaf foo3 { type uint32; }\n" - " notification n2;}"; + " notification n2;" + " md:annotation attr {type enumeration {enum val;}}" + "}"; UTEST_SETUP; @@ -122,6 +126,7 @@ static void test_anydata(void **state) { const char *data; + char *str; struct lyd_node *tree; data = "<any xmlns=\"urn:tests:a\">\n" @@ -145,6 +150,49 @@ test_anydata(void **state) "</any>\n"; CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + + assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str)); + lyd_free_all(tree); + + assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL)); + free(str); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + lyd_free_all(tree); +} + +static void +test_anyxml(void **state) +{ + const char *data; + char *str; + struct lyd_node *tree; + + data = "<anyx xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <element2 x:attr2=\"test\" xmlns:x=\"urn:x\">data</element2>\n" + " </element1>\n" + " <element1a/>\n" + "</anyx>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + + const char *data_expected = + "<anyx xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <element2 xmlns:x=\"urn:x\" x:attr2=\"test\">data</element2>\n" + " </element1>\n" + " <element1a/>\n" + "</anyx>\n"; + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + + assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str)); + lyd_free_all(tree); + + assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL)); + free(str); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); lyd_free_all(tree); } @@ -170,17 +218,21 @@ test_list(void **state) /* missing keys */ PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "List instance is missing its key \"a\".", "Data location \"/a:l1[b='b'][c='1']\", line number 1."); + CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL); PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "List instance is missing its key \"b\".", "Data location \"/a:l1[a='a']\", line number 1."); PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><b>b</b><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "List instance is missing its key \"c\".", "Data location \"/a:l1[a='a'][b='b']\", line number 1."); + CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL); /* key duplicate */ PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b><a>a</a><c>1</c></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Duplicate instance of \"c\".", "Data location \"/a:l1[a='a'][b='b'][c='1'][c='1']/c\", line number 1."); + CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL); + CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL); /* keys order */ CHECK_PARSE_LYD("<l1 xmlns=\"urn:tests:a\"><d>d</d><a>a</a><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree); @@ -209,6 +261,7 @@ test_list(void **state) assert_non_null(leaf = (struct lyd_node_term *)leaf->next); CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0); CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL); + CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL); lyd_free_all(tree); PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, @@ -250,14 +303,14 @@ test_opaq(void **state) /* opaq flag */ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "foo3", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "foo3", 0, 0, NULL, 1, ""); CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<foo3 xmlns=\"urn:tests:a\"/>\n"); lyd_free_all(tree); /* list, opaq flag */ data = "<l1 xmlns=\"urn:tests:a\"/>"; CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<l1 xmlns=\"urn:tests:a\"/>\n"); lyd_free_all(tree); @@ -273,7 +326,7 @@ test_opaq(void **state) /* opaq flag */ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); lyd_free_all(tree); @@ -289,7 +342,7 @@ test_opaq(void **state) /* opaq flag */ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); lyd_free_all(tree); @@ -300,7 +353,7 @@ test_opaq(void **state) " <c xmld:id=\"D\">1</c>\n" "</a>\n", LYD_XML, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree)); - CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "Line number 3."); + CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "Data location \"/a\", line number 3."); } static void @@ -358,10 +411,10 @@ test_rpc(void **state) node = lyd_child(node); /* z has no value */ - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 1, ""); node = node->parent->next; /* l1 key c has invalid value so it is at the end */ - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" @@ -554,10 +607,10 @@ test_netconf_rpc(void **state) node = lyd_child(node); /* z has no value */ - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 1, ""); node = node->parent->next; /* l1 key c has invalid value so it is at the end */ - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, ""); CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\"/>\n"); @@ -581,8 +634,33 @@ test_netconf_rpc(void **state) lyd_free_all(tree); lyd_free_all(op); - /* wrong namespace, element name, whatever... */ - /* TODO */ + /* invalid anyxml nested metadata value */ + data = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"1\" pid=\"4114692032\">\n" + " <copy-config>\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <source>\n" + " <config>\n" + " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>5</c>\n" + " <cont nc:operation=\"merge\">\n" + " <e nc:operation=\"merge2\">false</e>\n" + " </cont>\n" + " </l1>\n" + " </config>\n" + " </source>\n" + " </copy-config>\n" + "</rpc>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_NETCONF, &tree, &op)); + ly_in_free(in, 0); + CHECK_LOG_CTX("Invalid enumeration value \"merge2\".", + "Path \"/ietf-netconf:copy-config/source/config/a:l1[a='val_a'][b='val_b'][c='5']/cont/e/@ietf-netconf:operation\", line number 13."); + lyd_free_all(tree); + assert_null(op); } static void @@ -678,6 +756,22 @@ test_netconf_reply_or_notification(void **state) lyd_free_all(tree); lyd_free_all(op2); + /* notification with a different order */ + data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + "<c xmlns=\"urn:tests:a\">\n" + " <n1>\n" + " <nl>value</nl>\n" + " </n1>\n" + "</c>\n" + "<eventTime>2010-12-06T08:00:01Z</eventTime>\n" + "</notification>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_NETCONF, &tree, &op2)); + ly_in_free(in, 0); + + lyd_free_all(tree); + lyd_free_all(op2); + /* parse a data reply */ data = "<rpc-reply message-id=\"55\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" " <al xmlns=\"urn:tests:a\">25</al>\n" @@ -739,6 +833,64 @@ test_netconf_reply_or_notification(void **state) } static void +test_restconf_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL))); + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree)); + + data = "<input xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">" + "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>" + "<config>" + "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>" + "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">" + "<a>val_a</a><b>val_b</b><c>val_c</c>" + "</l1>" + "</config></input>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_XML, LYD_TYPE_RPC_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* the same just connected to the edit-data RPC */ + data = "<edit-data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">" + "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>" + "<config>" + "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>" + "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">" + "<a>val_a</a><b>val_b</b><c>val_c</c>" + "</l1>" + "</config></edit-data>"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void +test_restconf_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *envp; + + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree)); + + data = "<output xmlns=\"urn:tests:a\"><al>25</al></output>"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_XML, LYD_TYPE_REPLY_RESTCONF, &envp, NULL)); + ly_in_free(in, 0); + + /* connected to the RPC with the parent */ + data = "<c xmlns=\"urn:tests:a\"><act><al>25</al></act></c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + lyd_free_all(envp); +} + +static void test_filter_attributes(void **state) { const char *data; @@ -812,12 +964,58 @@ test_data_skip(void **state) lyd_free_all(tree); } +static void +test_metadata(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid metadata value */ + data = "<c xmlns=\"urn:tests:a\" xmlns:a=\"urn:tests:a\"><x a:attr=\"value\">xval</x></c>"; + assert_int_equal(LY_EVALID, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + CHECK_LOG_CTX("Invalid enumeration value \"value\".", "Path \"/a:c/x/@a:attr\", line number 1."); +} + +static void +test_subtree(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree; + + /* prepare data with the parent */ + data = "<l1 xmlns=\"urn:tests:a\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>1</c>\n" + "</l1>\n"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + + /* parse a subtree of it */ + data = "<cont xmlns=\"urn:tests:a\">\n" + " <e>true</e>\n" + "</cont>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_data(UTEST_LYCTX, tree, in, LYD_XML, 0, LYD_VALIDATE_PRESENT, NULL)); + ly_in_free(in, 0); + + /* parse another container, fails */ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_EVALID, lyd_parse_data(UTEST_LYCTX, tree, in, LYD_XML, 0, LYD_VALIDATE_PRESENT, NULL)); + ly_in_free(in, 0); + CHECK_LOG_CTX("Duplicate instance of \"cont\".", "Data location \"/a:l1[a='val_a'][b='val_b'][c='1']/cont\"."); + + lyd_free_all(tree); +} + int main(void) { const struct CMUnitTest tests[] = { UTEST(test_leaf, setup), UTEST(test_anydata, setup), + UTEST(test_anyxml, setup), UTEST(test_list, setup), UTEST(test_container, setup), UTEST(test_opaq, setup), @@ -828,8 +1026,12 @@ main(void) UTEST(test_netconf_rpc, setup), UTEST(test_netconf_action, setup), UTEST(test_netconf_reply_or_notification, setup), + UTEST(test_restconf_rpc, setup), + UTEST(test_restconf_reply, setup), UTEST(test_filter_attributes, setup), UTEST(test_data_skip, setup), + UTEST(test_metadata, setup), + UTEST(test_subtree, setup), }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/tests/utests/data/test_printer_xml.c b/tests/utests/data/test_printer_xml.c index d533c41..6213a37 100644 --- a/tests/utests/data/test_printer_xml.c +++ b/tests/utests/data/test_printer_xml.c @@ -145,7 +145,8 @@ test_anydata(void **state) " <cont>\n" " <elem1 xmlns=\"urn:tests:defs\">\n" " <elem2 xmlns=\"urn:tests:types\" xmlns:defs=\"urn:tests:defs\" xmlns:defaults=\"urn:defaults\" " - "defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\"/>\n" + "defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\">\n" + " </elem2>\n" " </elem1>\n" " </cont>\n" "</any>\n"; diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c index 27dba42..494fdf3 100644 --- a/tests/utests/data/test_tree_data.c +++ b/tests/utests/data/test_tree_data.c @@ -1,9 +1,9 @@ /** * @file test_tree_data.c - * @author: Radek Krejci <rkrejci@cesnet.cz> - * @brief unit tests for functions from tress_data.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from tree_data.c * - * Copyright (c) 2018-2019 CESNET, z.s.p.o. + * Copyright (c) 2018-2023 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -100,7 +100,7 @@ test_compare(void **state) data2 = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); - assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, LYD_COMPARE_FULL_RECURSION)); assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next->next, tree2->next, 0)); lyd_free_all(tree1); lyd_free_all(tree2); @@ -145,6 +145,14 @@ test_compare(void **state) assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); lyd_free_all(tree1); lyd_free_all(tree2); + + data1 = "<c xmlns=\"urn:tests:a\"><x>c</x><x>a</x><x>b</x></c>"; + data2 = "<c xmlns=\"urn:tests:a\"><x>a</x><x>b</x><x>c</x></c>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); } static void @@ -196,7 +204,7 @@ test_compare_diff_ctx(void **state) data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1); CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2); - assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); lyd_free_all(tree1); lyd_free_all(tree2); @@ -210,7 +218,7 @@ test_compare_diff_ctx(void **state) data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1); CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2); - assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); lyd_free_all(tree1); lyd_free_all(tree2); @@ -329,8 +337,7 @@ test_dup(void **state) data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); - assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)((struct lyd_node_inner *)tree1->next)->child)->child, NULL, - LYD_DUP_WITH_PARENTS, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_dup_single(lyd_child(lyd_child(tree1->next)), NULL, LYD_DUP_WITH_PARENTS, &tree2)); int unsigned flag = LYS_CONFIG_R | LYS_SET_ENUM; CHECK_LYSC_NODE(tree2->schema, NULL, 0, flag, 1, "x", 1, LYS_LEAF, 1, 0, NULL, 0); @@ -352,8 +359,8 @@ test_dup(void **state) data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, 0, &tree2)); - assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)((struct lyd_node_inner *)tree1->next)->child)->child, - (struct lyd_node_inner *)tree2, LYD_DUP_WITH_PARENTS, NULL)); + assert_int_equal(LY_SUCCESS, lyd_dup_single(lyd_child(lyd_child(tree1->next)), (struct lyd_node_inner *)tree2, + LYD_DUP_WITH_PARENTS, NULL)); assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION)); lyd_free_all(tree1); lyd_free_all(tree2); @@ -363,6 +370,8 @@ test_dup(void **state) CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); assert_int_equal(LY_EINVAL, lyd_dup_single(((struct lyd_node_inner *)tree1)->child->prev, (struct lyd_node_inner *)tree1->next, LYD_DUP_WITH_PARENTS, NULL)); + CHECK_LOG_CTX("None of the duplicated node \"c\" schema parents match the provided parent \"c\".", + NULL); lyd_free_all(tree1); } @@ -488,6 +497,11 @@ test_find_path(void **state) assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='10.0.0.1']", 0, NULL)); assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='2100::1']", 0, NULL)); assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/pref[.='fc00::/64']", 0, NULL)); + + assert_int_equal(LY_EVALID, lyd_find_path(root, "/cont", 0, NULL)); + CHECK_LOG_CTX("Prefix missing for \"cont\" in path.", "Schema location \"/c:cont\"."); + assert_int_equal(LY_SUCCESS, lyd_find_path(root, "nexthop[gateway='2100::1']", 0, NULL)); + lyd_free_all(root); } @@ -525,6 +539,7 @@ test_data_hash(void **state) /* The run must not crash due to the assert that checks the hash. */ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"ll\".", "Data location \"/test-data-hash:c/ll[.='']\", line number 1."); lyd_free_all(tree); } diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c index 415e16a..d0dcae5 100644 --- a/tests/utests/data/test_validation.c +++ b/tests/utests/data/test_validation.c @@ -1154,6 +1154,92 @@ test_must(void **state) CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/i:cont/l3\".", "not-left"); } +static void +test_multi_error(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module ii {\n" + " namespace urn:tests:ii;\n" + " prefix ii;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " leaf l2 {\n" + " must \"../l = 'right'\";\n" + " type string;\n" + " }\n" + " leaf l3 {\n" + " must \"../l = 'left'\" {\n" + " error-app-tag \"not-left\";\n" + " error-message \"l leaf is not left\";\n" + " }\n" + " type string;\n" + " }\n" + " leaf-list ll {\n" + " type uint32;\n" + " min-elements 2;\n" + " }\n" + " }\n" + "}"; + const char *data; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* xml */ + data = + "<cont xmlns=\"urn:tests:ii\">\n" + " <l>wrong</l>\n" + " <l>wrong2</l>\n" + " <l2>val</l2>\n" + " <l3>val</l3>\n" + " <ll>ahoy</ll>\n" + "</cont>\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "Schema location \"/ii:cont/ll\".", "too-few-elements"); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/ii:cont/l3\".", "not-left"); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "Data location \"/ii:cont/l2\".", "must-violation"); + CHECK_LOG_CTX_APPTAG("Invalid type uint32 value \"ahoy\".", "Data location \"/ii:cont/ll\", line number 6.", NULL); + + /* json */ + data = "{\n" + " \"ii:cont\": {\n" + " \"l\": \"wrong\",\n" + " \"l\": \"wrong2\",\n" + " \"l2\": \"val\",\n" + " \"l3\": \"val\",\n" + " \"ll\": [\"ahoy\"]\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "Schema location \"/ii:cont/ll\".", "too-few-elements"); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/ii:cont/l3\".", "not-left"); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "Data location \"/ii:cont/l2\".", "must-violation"); + CHECK_LOG_CTX_APPTAG("Invalid non-number-encoded uint32 value \"ahoy\".", "Data location \"/ii:cont/ll\", line number 7.", NULL); + + /* validation */ + data = "{\n" + " \"ii:cont\": {\n" + " \"l\": \"wrong\",\n" + " \"l\": \"wrong2\",\n" + " \"l2\": \"val\",\n" + " \"l3\": \"val\",\n" + " \"ll\": [25]\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, NULL)); + lyd_free_tree(tree); + CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "Schema location \"/ii:cont/ll\".", "too-few-elements"); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/ii:cont/l3\".", "not-left"); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "Data location \"/ii:cont/l2\".", "must-violation"); + CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "Data location \"/ii:cont/l\".", NULL); + CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "Data location \"/ii:cont/l\".", NULL); +} + const char *schema_j = "module j {\n" " namespace urn:tests:j;\n" @@ -1212,6 +1298,7 @@ test_action(void **state) struct lyd_node *tree, *op_tree; UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL); + UTEST_LOG_CTX_CLEAN; assert_int_equal(LY_SUCCESS, ly_in_new_memory( "<cont xmlns=\"urn:tests:j\">\n" @@ -1304,6 +1391,7 @@ test_reply(void **state) struct lyd_node *tree, *op_tree; UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL); + UTEST_LOG_CTX_CLEAN; assert_int_equal(LY_SUCCESS, ly_in_new_memory( "<cont xmlns=\"urn:tests:j\">\n" @@ -1422,7 +1510,7 @@ test_case(void **state) " }\n" "}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.", - "Data location \"/k:ch\", line number 5."); + "Data location \"/k:ch\", line number 6."); CHECK_PARSE_LYD_PARAM( "{\n" @@ -1432,7 +1520,7 @@ test_case(void **state) " }\n" "}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.", - "Data location \"/k:ch\", line number 5."); + "Data location \"/k:ch\", line number 6."); } int @@ -1450,6 +1538,7 @@ main(void) UTEST(test_defaults), UTEST(test_state), UTEST(test_must), + UTEST(test_multi_error), UTEST(test_action), UTEST(test_rpc), UTEST(test_reply), |