diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:55:11 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:55:11 +0000 |
commit | cd07912073c951b4bbb871ed2653af1be2cfc714 (patch) | |
tree | 1073c2308492e6aea4c66cb7436ee92db2abfd42 /tests/utests/data | |
parent | Initial commit. (diff) | |
download | libyang2-cd07912073c951b4bbb871ed2653af1be2cfc714.tar.xz libyang2-cd07912073c951b4bbb871ed2653af1be2cfc714.zip |
Adding upstream version 2.1.30.upstream/2.1.30upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/utests/data')
-rw-r--r-- | tests/utests/data/test_diff.c | 1221 | ||||
-rw-r--r-- | tests/utests/data/test_lyb.c | 2841 | ||||
-rw-r--r-- | tests/utests/data/test_merge.c | 756 | ||||
-rw-r--r-- | tests/utests/data/test_new.c | 446 | ||||
-rw-r--r-- | tests/utests/data/test_parser_json.c | 793 | ||||
-rw-r--r-- | tests/utests/data/test_parser_xml.c | 836 | ||||
-rw-r--r-- | tests/utests/data/test_printer_xml.c | 343 | ||||
-rw-r--r-- | tests/utests/data/test_tree_data.c | 597 | ||||
-rw-r--r-- | tests/utests/data/test_validation.c | 1460 |
9 files changed, 9293 insertions, 0 deletions
diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c new file mode 100644 index 0000000..1b7592a --- /dev/null +++ b/tests/utests/data/test_diff.c @@ -0,0 +1,1221 @@ +/** + * @file test_diff.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief tests for lyd_diff() + * + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#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_LYD_STRING(IN_MODEL, TEXT) \ + CHECK_LYD_STRING_PARAM(IN_MODEL, 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 TEST_DIFF_3(XML1, XML2, XML3, DIFF1, DIFF2, MERGE) \ + { \ + struct lyd_node *data1;\ + struct lyd_node *data2;\ + struct lyd_node *data3;\ + /*create*/\ + CHECK_PARSE_LYD(XML1, data1);\ + CHECK_PARSE_LYD(XML2, data2);\ + CHECK_PARSE_LYD(XML3, data3);\ + /* diff1 */ \ + struct lyd_node *diff1;\ + CHECK_PARSE_LYD_DIFF(data1, data2, diff1); \ + CHECK_LYD_STRING(diff1, DIFF1); \ + assert_int_equal(lyd_diff_apply_all(&data1, diff1), LY_SUCCESS); \ + CHECK_LYD(data1, data2); \ + /* diff2 */ \ + struct lyd_node *diff2;\ + CHECK_PARSE_LYD_DIFF(data2, data3, diff2); \ + CHECK_LYD_STRING(diff2, DIFF2); \ + assert_int_equal(lyd_diff_apply_all(&data2, diff2), LY_SUCCESS);\ + CHECK_LYD(data2, data3);\ + /* merge */ \ + assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS);\ + CHECK_LYD_STRING(diff1, MERGE); \ + /* cleanup */ \ + lyd_free_all(data1);\ + lyd_free_all(data2);\ + lyd_free_all(data3);\ + lyd_free_all(diff1);\ + lyd_free_all(diff2);\ + } + +const char *schema1 = + "module defaults {\n" + " yang-version 1.1;\n" + " namespace \"urn:libyang:tests:defaults\";\n" + " prefix df;\n" + "" + " feature unhide;\n" + "" + " typedef defint32 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf hiddenleaf {\n" + " if-feature \"unhide\";\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " container df {\n" + " leaf foo {\n" + " type defint32;\n" + " }\n" + "" + " leaf hiddenleaf {\n" + " if-feature \"unhide\";\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " container bar {\n" + " presence \"\";\n" + " leaf hi {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf ho {\n" + " type int32;\n" + " mandatory true;\n" + " }\n" + " }\n" + "" + " leaf-list llist {\n" + " type defint32;\n" + " ordered-by user;\n" + " }\n" + "" + " list ul {\n" + " key \"l1\";\n" + " ordered-by user;\n" + " leaf l1 {\n" + " type string;\n" + " }\n" + "" + " leaf l2 {\n" + " type int32;\n" + " }\n" + " }\n" + "" + " leaf-list dllist {\n" + " type uint8;\n" + " default \"1\";\n" + " default \"2\";\n" + " default \"3\";\n" + " }\n" + "" + " list list {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + "" + " leaf value {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + " list list2 {\n" + " key \"name2\";\n" + " leaf name2 {\n" + " type string;\n" + " }\n" + " leaf value2 {\n" + " type int32;\n" + " }\n" + " }\n" + " }\n"; +const char *schema2 = + " choice select {\n" + " default \"a\";\n" + " case a {\n" + " choice a {\n" + " leaf a1 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf a2 {\n" + " type int32;\n" + " default \"24\";\n" + " }\n" + " }\n" + " }\n" + "" + " leaf b {\n" + " type string;\n" + " }\n" + "" + " container c {\n" + " presence \"\";\n" + " leaf x {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + " }\n" + " }\n" + "" + " choice select2 {\n" + " default \"s2b\";\n" + " leaf s2a {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " case s2b {\n" + " choice s2b {\n" + " default \"b1\";\n" + " case b1 {\n" + " leaf b1_1 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf b1_2 {\n" + " type string;\n" + " }\n" + "" + " leaf b1_status {\n" + " type int32;\n" + " default \"42\";\n" + " config false;\n" + " }\n" + " }\n" + "" + " leaf b2 {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + " }\n" + " }\n" + " }\n" + " list kl {\n" + " config \"false\";\n" + " leaf l1 {\n" + " type string;\n" + " }\n" + "" + " leaf l2 {\n" + " type int32;\n" + " }\n" + " }\n" + "" + " leaf-list kll {\n" + " config \"false\";\n" + " type string;\n" + " }\n" + " }\n" + "" + " container hidden {\n" + " leaf foo {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf baz {\n" + " type int32;\n" + " default \"42\";\n" + " }\n" + "" + " leaf papa {\n" + " type int32;\n" + " default \"42\";\n" + " config false;\n" + " }\n" + " }\n" + "" + " rpc rpc1 {\n" + " input {\n" + " leaf inleaf1 {\n" + " type string;\n" + " }\n" + "" + " leaf inleaf2 {\n" + " type string;\n" + " default \"def1\";\n" + " }\n" + " }\n" + "" + " output {\n" + " leaf outleaf1 {\n" + " type string;\n" + " default \"def2\";\n" + " }\n" + "" + " leaf outleaf2 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "" + " notification notif {\n" + " leaf ntfleaf1 {\n" + " type string;\n" + " default \"def3\";\n" + " }\n" + "" + " leaf ntfleaf2 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + +static int +setup(void **state) +{ + char *schema; + + UTEST_SETUP; + + /* create one schema, longer than 4095 chars */ + schema = malloc(strlen(schema1) + strlen(schema2) + 1); + strcpy(schema, schema1); + strcat(schema, schema2); + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + free(schema); + + return 0; +} + +static void +test_invalid(void **state) +{ + (void) state; + const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\"><foo>42</foo></df>"; + + struct lyd_node *model_1; + + CHECK_PARSE_LYD(xml, model_1); + + struct lyd_node *diff = NULL; + + assert_int_equal(lyd_diff_siblings(model_1, lyd_child(model_1), 0, &diff), LY_EINVAL); + assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, NULL), LY_EINVAL); + + lyd_free_all(model_1); + lyd_free_all(diff); +} + +static void +test_same(void **state) +{ + (void) state; + const char *xml = + "<nacm xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-acm\">\n" + " <enable-nacm>true</enable-nacm>\n" + " <read-default>permit</read-default>\n" + " <write-default>deny</write-default>\n" + " <exec-default>permit</exec-default>\n" + " <enable-external-groups>true</enable-external-groups>\n" + "</nacm><df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo><b1_1>42</b1_1>\n" + "</df><hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo><baz>42</baz></hidden>\n"; + + struct lyd_node *model_1; + struct lyd_node *model_2; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-acm", "2018-02-14", NULL)); + + CHECK_PARSE_LYD(xml, model_1); + CHECK_PARSE_LYD(xml, model_2); + + struct lyd_node *diff = NULL; + + assert_int_equal(lyd_diff_siblings(model_1, model_2, 0, &diff), LY_SUCCESS); + assert_null(diff); + assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS); + CHECK_LYD(model_1, model_2); + + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); +} + +static void +test_empty1(void **state) +{ + (void) state; + const char *xml_in = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"; + + struct lyd_node *model_1 = NULL; + struct lyd_node *model_2; + + CHECK_PARSE_LYD(xml_in, model_2); + + struct lyd_node *diff; + + CHECK_PARSE_LYD_DIFF(model_1, model_2, diff); + CHECK_LYD_STRING(diff, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"); + assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS); + CHECK_LYD(model_1, model_2); + + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(diff); +} + +static void +test_empty2(void **state) +{ + (void) state; + const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df><hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"; + + struct lyd_node *model_1; + + CHECK_PARSE_LYD(xml, model_1); + + struct lyd_node *diff; + + CHECK_PARSE_LYD_DIFF(model_1, NULL, diff); + CHECK_LYD_STRING(diff, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n" + " <foo>42</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"); + + assert_int_equal(lyd_diff_apply_all(&model_1, diff), LY_SUCCESS); + assert_ptr_equal(model_1, NULL); + + lyd_free_all(diff); + lyd_free_all(model_1); +} + +static void +test_empty_nested(void **state) +{ + (void) state; + const char *xml = "<df xmlns=\"urn:libyang:tests:defaults\"><foo>42</foo></df>"; + + struct lyd_node *model_1; + + CHECK_PARSE_LYD(xml, model_1); + + struct lyd_node *diff = NULL; + + assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, &diff), LY_SUCCESS); + assert_null(diff); + + struct lyd_node *diff1; + + CHECK_PARSE_LYD_DIFF(NULL, lyd_child(model_1), diff1); + CHECK_LYD_STRING(diff1, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"create\">42</foo>\n" + "</df>\n"); + + struct lyd_node *diff2; + + CHECK_PARSE_LYD_DIFF(lyd_child(model_1), NULL, diff2); + CHECK_LYD_STRING(diff2, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"delete\">42</foo>\n" + "</df>\n"); + + lyd_free_all(model_1); + lyd_free_all(diff1); + lyd_free_all(diff2); +} + +static void +test_delete_merge(void **state) +{ + (void) state; + struct lyd_node *diff1, *diff2; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <list>\n" + " <name>a</name>\n" + " <list2 yang:operation=\"delete\">\n" + " <name2>a</name2>\n" + " </list2>\n" + " </list>\n" + "</df>\n"; + const char *xml2 = + "<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>a</name>\n" + " </list>\n" + "</df>\n"; + const char *xml_merge = + "<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>a</name>\n" + " <list2 yang:operation=\"delete\">\n" + " <name2>a</name2>\n" + " </list2>\n" + " </list>\n" + "</df>\n"; + + CHECK_PARSE_LYD(xml1, diff1); + CHECK_PARSE_LYD(xml2, diff2); + + assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS); + CHECK_LYD_STRING(diff1, xml_merge); + + lyd_free_all(diff1); + lyd_free_all(diff2); +} + +static void +test_leaf(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>41</foo>\n" + " <b1_1>42</b1_1>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>40</foo>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>40</foo>\n" + "</hidden>\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" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"42\">41</foo>\n" + " <b1_1 yang:operation=\"create\">42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">\n" + " <foo>42</foo>\n" + " <baz>42</baz>\n" + "</hidden>\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" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"41\">40</foo>\n" + " <b1_1 yang:operation=\"delete\">42</b1_1>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <foo>40</foo>\n" + "</hidden>\n"; + + const char *out_merge = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"42\">40</foo>\n" + "</df>\n" + "<hidden xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-value=\"42\" yang:orig-default=\"false\">40</foo>\n" + " <baz yang:operation=\"delete\">42</baz>\n" + "</hidden>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_list(void **state) +{ + (void) state; + const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <list>\n" + " <name>a</name>\n" + " <value>1</value>\n" + " </list>\n" + " <list>\n" + " <name>b</name>\n" + " <value>2</value>\n" + " </list>\n" + "</df>\n"; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <list>\n" + " <name>b</name>\n" + " <value>-2</value>\n" + " </list>\n" + " <list>\n" + " <name>c</name>\n" + " <value>3</value>\n" + " </list>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <list>\n" + " <name>b</name>\n" + " <value>-2</value>\n" + " </list>\n" + " <list>\n" + " <name>a</name>\n" + " <value>2</value>\n" + " </list>\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" + " <list yang:operation=\"delete\">\n" + " <name>a</name>\n" + " <value>1</value>\n" + " </list>\n" + " <list yang:operation=\"none\">\n" + " <name>b</name>\n" + " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\">-2</value>\n" + " </list>\n" + " <list yang:operation=\"create\">\n" + " <name>c</name>\n" + " <value>3</value>\n" + " </list>\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" + " <list yang:operation=\"delete\">\n" + " <name>c</name>\n" + " <value>3</value>\n" + " </list>\n" + " <list yang:operation=\"create\">\n" + " <name>a</name>\n" + " <value>2</value>\n" + " </list>\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" + " <list yang:operation=\"none\">\n" + " <name>a</name>\n" + " <value yang:operation=\"replace\" yang:orig-value=\"1\" yang:orig-default=\"false\">2</value>\n" + " </list>\n" + " <list yang:operation=\"none\">\n" + " <name>b</name>\n" + " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\">-2</value>\n" + " </list>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_llist(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>2</llist>\n" + " <llist>3</llist>\n" + " <llist>4</llist>\n" + " <llist>5</llist>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\n" + " <llist>2</llist>\n" + " <llist>5</llist>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>5</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\n" + " <llist>2</llist>\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" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"1\">4</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"4\">3</llist>\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" + " <llist yang:operation=\"delete\" yang:orig-value=\"\">1</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"\">5</llist>\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" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"1\">4</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"4\">3</llist>\n" + " <llist yang:orig-value=\"\" yang:operation=\"delete\">1</llist>\n" + " <llist yang:orig-default=\"false\" yang:orig-value=\"2\" yang:value=\"\" yang:operation=\"replace\">5</llist>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_llist2(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <list><name>a</name><value>1</value></list>\n" + " <llist>2</llist>\n" + " <llist>3</llist>\n" + " <llist>4</llist>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <list><name>a</name><value>1</value></list>\n" + " <llist>2</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>4</llist>\n" + " <llist>1</llist>\n" + " <list><name>a</name><value>1</value></list>\n" + " <llist>3</llist>\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" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"2\">4</llist>\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" + " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">4</llist>\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" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\">4</llist>\n" + " <llist yang:orig-value=\"1\" yang:operation=\"delete\">2</llist>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_mix(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>2</llist>\n" + " <llist>3</llist>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>3</llist>\n" + " <llist>1</llist>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <llist>1</llist>\n" + " <llist>4</llist>\n" + " <llist>3</llist>\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" + " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">3</llist>\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" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\">1</llist>\n" + " <llist yang:operation=\"create\" yang:value=\"1\">4</llist>\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" + " <llist yang:operation=\"delete\" yang:orig-value=\"1\">2</llist>\n" + " <llist yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\" yang:value=\"\">3</llist>\n" + " <llist yang:orig-default=\"false\" yang:orig-value=\"3\" yang:value=\"\" yang:operation=\"replace\">1</llist>\n" + " <llist yang:value=\"1\" yang:operation=\"create\">4</llist>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_list(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" + " <l2>3</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml2 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>a</l1>\n" + " <l2>11</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml3 = + "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <ul>\n" + " <l1>c</l1>\n" + " <l2>33</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>\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"1\">11</l2>\n" + " </ul>\n" + " <ul yang:operation=\"delete\" yang:orig-key=\"[l1='a']\">\n" + " <l1>b</l1>\n" + " <l2>2</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=\"delete\" yang:orig-key=\"\">\n" + " <l1>a</l1>\n" + " <l2>11</l2>\n" + " </ul>\n" + " <ul yang:operation=\"none\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\">33</l2>\n" + " </ul>\n" + " <ul yang:operation=\"create\" yang:key=\"[l1='c']\">\n" + " <l1>b</l1>\n" + " <l2>2</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=\"delete\">\n" + " <l1>a</l1>\n" + " <l2 yang:operation=\"delete\">1</l2>\n" + " </ul>\n" + " <ul yang:orig-key=\"[l1='a']\" yang:operation=\"replace\" yang:key=\"[l1='c']\">\n" + " <l1>b</l1>\n" + " </ul>\n" + " <ul yang:operation=\"none\">\n" + " <l1>c</l1>\n" + " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"3\">33</l2>\n" + " </ul>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_userord_list2(void **state) +{ + (void) state; + const char *xml1 = + "<df xmlns=\"urn:libyang:tests:defaults\">\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" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>4</l2>\n" + " </ul>\n" + "</df>\n"; + const char *xml3 = + "<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" + " <l2>3</l2>\n" + " </ul>\n" + " <ul>\n" + " <l1>d</l1>\n" + " <l2>4</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=\"create\" yang:key=\"\">\n" + " <l1>c</l1>\n" + " <l2>3</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=\"create\" yang:key=\"\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul yang:operation=\"create\" yang:key=\"[l1='a']\">\n" + " <l1>b</l1>\n" + " <l2>2</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=\"create\" yang:key=\"\">\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </ul>\n" + " <ul yang:key=\"\" yang:operation=\"create\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </ul>\n" + " <ul yang:key=\"[l1='a']\" yang:operation=\"create\">\n" + " <l1>b</l1>\n" + " <l2>2</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; + const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kl>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </kl>\n" + "</df>\n"; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kl>\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kl>\n" + " <l1>c</l1>\n" + " </kl>\n" + " <kl>\n" + " <l2>4</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>e</l1>\n" + " <l2>5</l2>\n" + " </kl>\n" + " <kl>\n" + " <l1>f</l1>\n" + " <l2>6</l2>\n" + " </kl>\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" + " <kl yang:operation=\"delete\" yang:orig-position=\"2\">\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </kl>\n" + " <kl yang:operation=\"replace\" yang:position=\"\" yang:orig-position=\"1\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"2\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\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" + " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl yang:operation=\"delete\" yang:orig-position=\"\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"\">\n" + " <l1>c</l1>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"1\">\n" + " <l2>4</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"2\">\n" + " <l1>e</l1>\n" + " <l2>5</l2>\n" + " </kl>\n" + " <kl yang:operation=\"create\" yang:position=\"3\">\n" + " <l1>f</l1>\n" + " <l2>6</l2>\n" + " </kl>\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" + " <kl yang:operation=\"delete\" yang:orig-position=\"2\">\n" + " <l1>c</l1>\n" + " <l2>3</l2>\n" + " </kl>\n" + " <kl yang:orig-position=\"1\" yang:operation=\"delete\">\n" + " <l1>b</l1>\n" + " <l2>2</l2>\n" + " </kl>\n" + " <kl yang:orig-position=\"\" yang:operation=\"delete\">\n" + " <l1>a</l1>\n" + " <l2>1</l2>\n" + " </kl>\n" + " <kl yang:position=\"\" yang:operation=\"create\">\n" + " <l1>c</l1>\n" + " </kl>\n" + " <kl yang:position=\"1\" yang:operation=\"create\">\n" + " <l2>4</l2>\n" + " </kl>\n" + " <kl yang:position=\"2\" yang:operation=\"create\">\n" + " <l1>e</l1>\n" + " <l2>5</l2>\n" + " </kl>\n" + " <kl yang:position=\"3\" yang:operation=\"create\">\n" + " <l1>f</l1>\n" + " <l2>6</l2>\n" + " </kl>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_state_llist(void **state) +{ + (void) state; + const char *xml1 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kll>a</kll>\n" + " <kll>b</kll>\n" + " <kll>c</kll>\n" + "</df>\n"; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kll>b</kll>\n" + " <kll>c</kll>\n" + " <kll>a</kll>\n" + " <kll>a</kll>\n" + " <kll>a</kll>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <kll>a</kll>\n" + " <kll>d</kll>\n" + " <kll>a</kll>\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" + " <kll yang:operation=\"replace\" yang:orig-default=\"false\" yang:position=\"\" yang:orig-position=\"1\">b</kll>\n" + " <kll yang:operation=\"replace\" yang:orig-default=\"false\" yang:position=\"1\" yang:orig-position=\"2\">c</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"3\">a</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"4\">a</kll>\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" + " <kll yang:operation=\"delete\" yang:orig-position=\"\">b</kll>\n" + " <kll yang:operation=\"delete\" yang:orig-position=\"\">c</kll>\n" + " <kll yang:operation=\"delete\" yang:orig-position=\"2\">a</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"1\">d</kll>\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" + " <kll yang:orig-default=\"false\" yang:orig-position=\"1\" yang:operation=\"delete\">b</kll>\n" + " <kll yang:orig-default=\"false\" yang:orig-position=\"2\" yang:operation=\"delete\">c</kll>\n" + " <kll yang:operation=\"create\" yang:position=\"4\">a</kll>\n" + " <kll yang:position=\"1\" yang:operation=\"create\">d</kll>\n" + "</df>\n"; + + TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); +} + +static void +test_wd(void **state) +{ + (void) state; + const struct lys_module *mod; + const char *xml2 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>41</foo>\n" + " <dllist>4</dllist>\n" + "</df>\n"; + const char *xml3 = "<df xmlns=\"urn:libyang:tests:defaults\">\n" + " <foo>42</foo>\n" + " <dllist>4</dllist>\n" + " <dllist>1</dllist>\n" + "</df>\n"; + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "defaults"); + assert_non_null(mod); + + struct lyd_node *model_1 = NULL; + + assert_int_equal(lyd_validate_module(&model_1, mod, 0, NULL), LY_SUCCESS); + assert_ptr_not_equal(model_1, NULL); + + struct lyd_node *model_2; + struct lyd_node *model_3; + + CHECK_PARSE_LYD_PARAM(xml2, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_2); + CHECK_PARSE_LYD_PARAM(xml3, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, model_3); + + /* diff1 */ + struct lyd_node *diff1 = NULL; + + assert_int_equal(lyd_diff_siblings(model_1, model_2, LYD_DIFF_DEFAULTS, &diff1), LY_SUCCESS); + assert_non_null(diff1); + + const char *diff1_out_1 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"true\" yang:orig-value=\"42\">41</foo>\n" + " <dllist yang:operation=\"delete\">1</dllist>\n" + " <dllist yang:operation=\"delete\">2</dllist>\n" + " <dllist yang:operation=\"delete\">3</dllist>\n" + " <dllist yang:operation=\"create\">4</dllist>\n" + "</df>\n"; + + CHECK_LYD_STRING_PARAM(diff1, diff1_out_1, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL); + assert_int_equal(lyd_diff_apply_all(&model_1, diff1), LY_SUCCESS); + CHECK_LYD(model_1, model_2); + + /* diff2 */ + struct lyd_node *diff2; + + assert_int_equal(lyd_diff_siblings(model_2, model_3, LYD_DIFF_DEFAULTS, &diff2), LY_SUCCESS); + assert_non_null(diff2); + CHECK_LYD_STRING(diff2, + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"41\">42</foo>\n" + " <dllist yang:operation=\"create\">1</dllist>\n" + "</df>\n"); + + assert_int_equal(lyd_diff_apply_all(&model_2, diff2), LY_SUCCESS); + CHECK_LYD(model_2, model_3); + + /* merge */ + assert_int_equal(lyd_diff_merge_all(&diff1, diff2, 0), LY_SUCCESS); + + const char *diff1_out_2 = + "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <foo yang:orig-default=\"true\" yang:operation=\"none\">42</foo>\n" + " <dllist yang:operation=\"none\" yang:orig-default=\"true\">1</dllist>\n" + " <dllist yang:operation=\"delete\">2</dllist>\n" + " <dllist yang:operation=\"delete\">3</dllist>\n" + " <dllist yang:operation=\"create\">4</dllist>\n" + "</df>\n"; + + CHECK_LYD_STRING_PARAM(diff1, diff1_out_2, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL); + + lyd_free_all(model_1); + lyd_free_all(model_2); + lyd_free_all(model_3); + lyd_free_all(diff1); + lyd_free_all(diff2); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_invalid, setup), + UTEST(test_same, setup), + UTEST(test_empty1, setup), + UTEST(test_empty2, setup), + UTEST(test_empty_nested, setup), + UTEST(test_delete_merge, setup), + UTEST(test_leaf, setup), + UTEST(test_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_keyless_list, setup), + UTEST(test_state_llist, setup), + UTEST(test_wd, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_lyb.c b/tests/utests/data/test_lyb.c new file mode 100644 index 0000000..26f3e73 --- /dev/null +++ b/tests/utests/data/test_lyb.c @@ -0,0 +1,2841 @@ +/** + * @file test_lyb.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief Cmocka tests for LYB binary data format. + * + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "hash_table.h" +#include "libyang.h" + +#define CHECK_PARSE_LYD(INPUT, OUT_NODE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, OUT_NODE) + +#define CHECK_LYD_STRING(MODEL, TEXT) \ + CHECK_LYD_STRING_PARAM(MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK) + +static void +check_print_parse(void **state, const char *data_xml) +{ + struct lyd_node *tree_1; + struct lyd_node *tree_2; + char *lyb_out; + + CHECK_PARSE_LYD(data_xml, tree_1); + assert_int_equal(lyd_print_mem(&lyb_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, lyb_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, + 0, &tree_2)); + assert_non_null(tree_2); + CHECK_LYD(tree_1, tree_2); + + free(lyb_out); + lyd_free_all(tree_1); + lyd_free_all(tree_2); +} + +static int +setup(void **state) +{ + UTEST_SETUP; + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + return 0; +} + +static void +tests_leaflist(void **state) +{ + const char *mod; + const char *data_xml; + + mod = + "module mod { namespace \"urn:test-leaflist\"; prefix m;" + " container cont {" + " presence \"\";" + " leaf-list ll {" + " type uint8;" + " }" + " }" + "}"; + UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL); + + data_xml = + "<cont xmlns=\"urn:test-leaflist\">\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-leaflist\">\n" + " <ll>1</ll>\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-leaflist\">\n" + " <ll>1</ll>\n" + " <ll>2</ll>\n" + "</cont>\n"; + check_print_parse(state, data_xml); +} + +static void +tests_list(void **state) +{ + const char *mod; + const char *data_xml; + + mod = + "module mod { namespace \"urn:test-list\"; prefix m;" + " container cont {" + " presence \"\";" + " list lst {" + " key \"lf\";" + " leaf lf {" + " type uint8;" + " }" + " }" + " }" + "}"; + UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL); + + data_xml = + "<cont xmlns=\"urn:test-list\">\n" + " <lst>" + " <lf>1</lf>" + " </lst>" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-list\">\n" + " <lst>" + " <lf>1</lf>" + " <lf>2</lf>" + " </lst>" + "</cont>\n"; + check_print_parse(state, data_xml); +} + +static void +tests_any(void **state) +{ + const char *mod; + const char *data_xml; + + mod = + "module mod { namespace \"urn:test-any\"; prefix m;" + " container cont {" + " presence \"\";" + " anyxml anxml;\n" + " }" + "}"; + UTEST_ADD_MODULE(mod, LYS_IN_YANG, NULL, NULL); + + data_xml = + "<cont xmlns=\"urn:test-any\">\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-any\">\n" + " <anxml><node>value</node></anxml>\n" + "</cont>\n"; + check_print_parse(state, data_xml); + + data_xml = + "<cont xmlns=\"urn:test-any\">\n" + " <anxml><node1>value</node1></anxml>\n" + " <anxml><node2>value</node2></anxml>\n" + "</cont>\n"; + check_print_parse(state, data_xml); +} + +static void +test_ietf_interfaces(void **state) +{ + const char *data_xml = + "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>eth0</name>\n" + " <description>Ethernet 0</description>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n" + " <enabled>true</enabled>\n" + " <mtu>1500</mtu>\n" + " <address>\n" + " <ip>192.168.2.100</ip>\n" + " <prefix-length>24</prefix-length>\n" + " </address>\n" + " </ipv4>\n" + " </interface>\n" + " <interface>\n" + " <name>eth1</name>\n" + " <description>Ethernet 1</description>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n" + " <enabled>true</enabled>\n" + " <mtu>1500</mtu>\n" + " <address>\n" + " <ip>10.10.1.5</ip>\n" + " <prefix-length>16</prefix-length>\n" + " </address>\n" + " </ipv4>\n" + " </interface>\n" + " <interface>\n" + " <name>gigaeth0</name>\n" + " <description>GigabitEthernet 0</description>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>false</enabled>\n" + " </interface>\n" + "</interfaces>\n"; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-ip", NULL, NULL)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL)); + + check_print_parse(state, data_xml); +} + +static void +test_origin(void **state) +{ + const char *origin_yang = + "module test-origin {" + " namespace \"urn:test-origin\";" + " prefix to;" + " import ietf-origin {" + " prefix or;" + " }" + "" + " container cont {" + " leaf leaf1 {" + " type string;" + " }" + " leaf leaf2 {" + " type string;" + " }" + " leaf leaf3 {" + " type uint8;" + " }" + " }" + "}"; + const char *data_xml = + "<cont xmlns=\"urn:test-origin\">\n" + " <leaf1 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:default\">value1</leaf1>\n" + " <leaf2>value2</leaf2>\n" + " <leaf3 xmlns:or=\"urn:ietf:params:xml:ns:yang:ietf-origin\" or:origin=\"or:system\">125</leaf3>\n" + "</cont>\n"; + + UTEST_ADD_MODULE(origin_yang, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, lys_set_implemented(ly_ctx_get_module_latest(UTEST_LYCTX, "ietf-origin"), NULL)); + + check_print_parse(state, data_xml); +} + +static void +test_statements(void **state) +{ + const char *links_yang = + "module links {\n" + " yang-version 1.1;\n" + " namespace \"urn:module2\";\n" + " prefix mod2;\n" + "\n" + " identity just-another-identity;\n" + "\n" + " leaf one-leaf {\n" + " type string;\n" + " }\n" + "\n" + " list list-for-augment {\n" + " key keyleaf;\n" + "\n" + " leaf keyleaf {\n" + " type string;\n" + " }\n" + "\n" + " leaf just-leaf {\n" + " type int32;\n" + " }\n" + " }\n" + "\n" + " leaf rleaf {\n" + " type string;\n" + " }\n" + "\n" + " leaf-list llist {\n" + " type string;\n" + " min-elements 0;\n" + " max-elements 100;\n" + " ordered-by user;\n" + " }\n" + "\n" + " grouping rgroup {\n" + " leaf rg1 {\n" + " type string;\n" + " }\n" + "\n" + " leaf rg2 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + const char *statements_yang = + "module statements {\n" + " namespace \"urn:module\";\n" + " prefix mod;\n" + " yang-version 1.1;\n" + "\n" + " import links {\n" + " prefix mod2;\n" + " }\n" + "\n" + " identity random-identity {\n" + " base \"mod2:just-another-identity\";\n" + " base \"another-identity\";\n" + " }\n" + "\n" + " identity another-identity {\n" + " base \"mod2:just-another-identity\";\n" + " }\n" + "\n" + " typedef percent {\n" + " type uint8 {\n" + " range \"0 .. 100\";\n" + " }\n" + " units percent;\n" + " }\n" + "\n" + " container ice-cream-shop {\n" + " container employees {\n" + " list employee {\n" + " config true;\n" + " key id;\n" + " unique name;\n" + " min-elements 0;\n" + " max-elements 100;\n" + "\n" + " leaf id {\n" + " type uint64;\n" + " mandatory true;\n" + " }\n" + "\n" + " leaf name {\n" + " type string;\n" + " }\n" + "\n" + " leaf age {\n" + " type uint32;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " container random {\n" + " choice switch {\n" + " case a {\n" + " leaf aleaf {\n" + " type string;\n" + " default aaa;\n" + " }\n" + " }\n" + "\n" + " case c {\n" + " leaf cleaf {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "\n" + " anyxml xml-data;\n" + " anydata any-data;\n" + " leaf-list leaflist {\n" + " type string;\n" + " min-elements 0;\n" + " max-elements 20;\n" + " ordered-by system;\n" + " }\n" + "\n" + " grouping group {\n" + " leaf g1 {\n" + " mandatory false;\n" + " type percent;\n" + " }\n" + "\n" + " leaf g2 {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " uses group;\n" + " uses mod2:rgroup;\n" + "\n" + " leaf lref {\n" + " type leafref {\n" + " path \"/mod2:one-leaf\";\n" + " }\n" + " }\n" + "\n" + " leaf iref {\n" + " type identityref {\n" + " base \"mod2:just-another-identity\";\n" + " }\n" + " }\n" + " }\n" + "\n" + " notification notif;\n" + "\n" + " augment \"/random\" {\n" + " leaf aug-leaf {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + const char *data_xml = + "<one-leaf xmlns=\"urn:module2\">reference leaf</one-leaf>\n" + "<ice-cream-shop xmlns=\"urn:module\">\n" + " <employees>\n" + " <employee>\n" + " <id>0</id>\n" + " <name>John Doe</name>\n" + " <age>28</age>\n" + " </employee>\n" + " <employee>\n" + " <id>1</id>\n" + " <name>Dohn Joe</name>\n" + " <age>20</age>\n" + " </employee>\n" + " </employees>\n" + "</ice-cream-shop>\n" + "<random xmlns=\"urn:module\">\n" + " <aleaf>string</aleaf>\n" + " <xml-data><anyxml>data</anyxml></xml-data>\n" + " <any-data><notif/></any-data>\n" + " <leaflist>l0</leaflist>\n" + " <leaflist>l1</leaflist>\n" + " <leaflist>l2</leaflist>\n" + " <g1>40</g1>\n" + " <g2>string</g2>\n" + " <aug-leaf>string</aug-leaf>\n" + " <rg1>string</rg1>\n" + " <rg2>string</rg2>\n" + " <lref>reference leaf</lref>\n" + " <iref>random-identity</iref>\n" + "</random>\n"; + + UTEST_ADD_MODULE(links_yang, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(statements_yang, LYS_IN_YANG, NULL, NULL); + + check_print_parse(state, data_xml); +} + +static void +test_opaq(void **state) +{ + const char *nc_feats[] = {"writable-running", NULL}; + const char *data_xml = + "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config>\n" + " <top xmlns=\"urn:ed\">\n" + " <first>TestFirst</first>\n" + " </top>\n" + " </config>\n" + "</edit-config>\n"; + struct ly_in *in; + struct lyd_node *tree_1; + struct lyd_node *tree_2; + char *xml_out; /* tree_2 */ + LY_ERR rc; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", NULL, nc_feats)); + + ly_in_new_memory(data_xml, &in); + rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree_1, NULL); + ly_in_free(in, 0); + assert_int_equal(rc, LY_SUCCESS); + + assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); + + ly_in_new_memory(xml_out, &in); + rc = lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_LYB, LYD_TYPE_RPC_YANG, &tree_2, NULL); + ly_in_free(in, 0); + assert_int_equal(rc, LY_SUCCESS); + + /* compare models */ + CHECK_LYD(tree_1, tree_2); + + /* clean */ + free(xml_out); + lyd_free_all(tree_1); + lyd_free_all(tree_2); +} + +static void +test_collisions(void **state) +{ + char *counters_yang, *data_xml; + + counters_yang = malloc(32768); + strcpy(counters_yang, + "module counters {\n" + " namespace \"urn:counters\";\n" + " prefix c;\n" + "\n" + " container stats {\n"); + strcat(counters_yang, + " leaf counter1 {\n" + " type uint64;\n" + " }\n" + " leaf counter2 {\n" + " type uint64;\n" + " }\n" + " leaf counter3 {\n" + " type uint64;\n" + " }\n" + " leaf counter4 {\n" + " type uint64;\n" + " }\n" + " leaf counter5 {\n" + " type uint64;\n" + " }\n" + " leaf counter6 {\n" + " type uint64;\n" + " }\n" + " leaf counter7 {\n" + " type uint64;\n" + " }\n" + " leaf counter8 {\n" + " type uint64;\n" + " }\n" + " leaf counter9 {\n" + " type uint64;\n" + " }\n" + " leaf counter10 {\n" + " type uint64;\n" + " }\n" + " leaf counter11 {\n" + " type uint64;\n" + " }\n" + " leaf counter12 {\n" + " type uint64;\n" + " }\n" + " leaf counter13 {\n" + " type uint64;\n" + " }\n" + " leaf counter14 {\n" + " type uint64;\n" + " }\n" + " leaf counter15 {\n" + " type uint64;\n" + " }\n" + " leaf counter16 {\n" + " type uint64;\n" + " }\n" + " leaf counter17 {\n" + " type uint64;\n" + " }\n" + " leaf counter18 {\n" + " type uint64;\n" + " }\n" + " leaf counter19 {\n" + " type uint64;\n" + " }\n" + " leaf counter20 {\n" + " type uint64;\n" + " }\n" + " leaf counter21 {\n" + " type uint64;\n" + " }\n" + " leaf counter22 {\n" + " type uint64;\n" + " }\n" + " leaf counter23 {\n" + " type uint64;\n" + " }\n" + " leaf counter24 {\n" + " type uint64;\n" + " }\n" + " leaf counter25 {\n" + " type uint64;\n" + " }\n" + " leaf counter26 {\n" + " type uint64;\n" + " }\n" + " leaf counter27 {\n" + " type uint64;\n" + " }\n" + " leaf counter28 {\n" + " type uint64;\n" + " }\n" + " leaf counter29 {\n" + " type uint64;\n" + " }\n" + " leaf counter30 {\n" + " type uint64;\n" + " }\n" + " leaf counter31 {\n" + " type uint64;\n" + " }\n" + " leaf counter32 {\n" + " type uint64;\n" + " }\n" + " leaf counter33 {\n" + " type uint64;\n" + " }\n" + " leaf counter34 {\n" + " type uint64;\n" + " }\n" + " leaf counter35 {\n" + " type uint64;\n" + " }\n" + " leaf counter36 {\n" + " type uint64;\n" + " }\n" + " leaf counter37 {\n" + " type uint64;\n" + " }\n" + " leaf counter38 {\n" + " type uint64;\n" + " }\n" + " leaf counter39 {\n" + " type uint64;\n" + " }\n" + " leaf counter40 {\n" + " type uint64;\n" + " }\n" + " leaf counter41 {\n" + " type uint64;\n" + " }\n" + " leaf counter42 {\n" + " type uint64;\n" + " }\n" + " leaf counter43 {\n" + " type uint64;\n" + " }\n" + " leaf counter44 {\n" + " type uint64;\n" + " }\n" + " leaf counter45 {\n" + " type uint64;\n" + " }\n" + " leaf counter46 {\n" + " type uint64;\n" + " }\n" + " leaf counter47 {\n" + " type uint64;\n" + " }\n" + " leaf counter48 {\n" + " type uint64;\n" + " }\n" + " leaf counter49 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter50 {\n" + " type uint64;\n" + " }\n" + " leaf counter51 {\n" + " type uint64;\n" + " }\n" + " leaf counter52 {\n" + " type uint64;\n" + " }\n" + " leaf counter53 {\n" + " type uint64;\n" + " }\n" + " leaf counter54 {\n" + " type uint64;\n" + " }\n" + " leaf counter55 {\n" + " type uint64;\n" + " }\n" + " leaf counter56 {\n" + " type uint64;\n" + " }\n" + " leaf counter57 {\n" + " type uint64;\n" + " }\n" + " leaf counter58 {\n" + " type uint64;\n" + " }\n" + " leaf counter59 {\n" + " type uint64;\n" + " }\n" + " leaf counter60 {\n" + " type uint64;\n" + " }\n" + " leaf counter61 {\n" + " type uint64;\n" + " }\n" + " leaf counter62 {\n" + " type uint64;\n" + " }\n" + " leaf counter63 {\n" + " type uint64;\n" + " }\n" + " leaf counter64 {\n" + " type uint64;\n" + " }\n" + " leaf counter65 {\n" + " type uint64;\n" + " }\n" + " leaf counter66 {\n" + " type uint64;\n" + " }\n" + " leaf counter67 {\n" + " type uint64;\n" + " }\n" + " leaf counter68 {\n" + " type uint64;\n" + " }\n" + " leaf counter69 {\n" + " type uint64;\n" + " }\n" + " leaf counter70 {\n" + " type uint64;\n" + " }\n" + " leaf counter71 {\n" + " type uint64;\n" + " }\n" + " leaf counter72 {\n" + " type uint64;\n" + " }\n" + " leaf counter73 {\n" + " type uint64;\n" + " }\n" + " leaf counter74 {\n" + " type uint64;\n" + " }\n" + " leaf counter75 {\n" + " type uint64;\n" + " }\n" + " leaf counter76 {\n" + " type uint64;\n" + " }\n" + " leaf counter77 {\n" + " type uint64;\n" + " }\n" + " leaf counter78 {\n" + " type uint64;\n" + " }\n" + " leaf counter79 {\n" + " type uint64;\n" + " }\n" + " leaf counter80 {\n" + " type uint64;\n" + " }\n" + " leaf counter81 {\n" + " type uint64;\n" + " }\n" + " leaf counter82 {\n" + " type uint64;\n" + " }\n" + " leaf counter83 {\n" + " type uint64;\n" + " }\n" + " leaf counter84 {\n" + " type uint64;\n" + " }\n" + " leaf counter85 {\n" + " type uint64;\n" + " }\n" + " leaf counter86 {\n" + " type uint64;\n" + " }\n" + " leaf counter87 {\n" + " type uint64;\n" + " }\n" + " leaf counter88 {\n" + " type uint64;\n" + " }\n" + " leaf counter89 {\n" + " type uint64;\n" + " }\n" + " leaf counter90 {\n" + " type uint64;\n" + " }\n" + " leaf counter91 {\n" + " type uint64;\n" + " }\n" + " leaf counter92 {\n" + " type uint64;\n" + " }\n" + " leaf counter93 {\n" + " type uint64;\n" + " }\n" + " leaf counter94 {\n" + " type uint64;\n" + " }\n" + " leaf counter95 {\n" + " type uint64;\n" + " }\n" + " leaf counter96 {\n" + " type uint64;\n" + " }\n" + " leaf counter97 {\n" + " type uint64;\n" + " }\n" + " leaf counter98 {\n" + " type uint64;\n" + " }\n" + " leaf counter99 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter100 {\n" + " type uint64;\n" + " }\n" + " leaf counter101 {\n" + " type uint64;\n" + " }\n" + " leaf counter102 {\n" + " type uint64;\n" + " }\n" + " leaf counter103 {\n" + " type uint64;\n" + " }\n" + " leaf counter104 {\n" + " type uint64;\n" + " }\n" + " leaf counter105 {\n" + " type uint64;\n" + " }\n" + " leaf counter106 {\n" + " type uint64;\n" + " }\n" + " leaf counter107 {\n" + " type uint64;\n" + " }\n" + " leaf counter108 {\n" + " type uint64;\n" + " }\n" + " leaf counter109 {\n" + " type uint64;\n" + " }\n" + " leaf counter110 {\n" + " type uint64;\n" + " }\n" + " leaf counter111 {\n" + " type uint64;\n" + " }\n" + " leaf counter112 {\n" + " type uint64;\n" + " }\n" + " leaf counter113 {\n" + " type uint64;\n" + " }\n" + " leaf counter114 {\n" + " type uint64;\n" + " }\n" + " leaf counter115 {\n" + " type uint64;\n" + " }\n" + " leaf counter116 {\n" + " type uint64;\n" + " }\n" + " leaf counter117 {\n" + " type uint64;\n" + " }\n" + " leaf counter118 {\n" + " type uint64;\n" + " }\n" + " leaf counter119 {\n" + " type uint64;\n" + " }\n" + " leaf counter120 {\n" + " type uint64;\n" + " }\n" + " leaf counter121 {\n" + " type uint64;\n" + " }\n" + " leaf counter122 {\n" + " type uint64;\n" + " }\n" + " leaf counter123 {\n" + " type uint64;\n" + " }\n" + " leaf counter124 {\n" + " type uint64;\n" + " }\n" + " leaf counter125 {\n" + " type uint64;\n" + " }\n" + " leaf counter126 {\n" + " type uint64;\n" + " }\n" + " leaf counter127 {\n" + " type uint64;\n" + " }\n" + " leaf counter128 {\n" + " type uint64;\n" + " }\n" + " leaf counter129 {\n" + " type uint64;\n" + " }\n" + " leaf counter130 {\n" + " type uint64;\n" + " }\n" + " leaf counter131 {\n" + " type uint64;\n" + " }\n" + " leaf counter132 {\n" + " type uint64;\n" + " }\n" + " leaf counter133 {\n" + " type uint64;\n" + " }\n" + " leaf counter134 {\n" + " type uint64;\n" + " }\n" + " leaf counter135 {\n" + " type uint64;\n" + " }\n" + " leaf counter136 {\n" + " type uint64;\n" + " }\n" + " leaf counter137 {\n" + " type uint64;\n" + " }\n" + " leaf counter138 {\n" + " type uint64;\n" + " }\n" + " leaf counter139 {\n" + " type uint64;\n" + " }\n" + " leaf counter140 {\n" + " type uint64;\n" + " }\n" + " leaf counter141 {\n" + " type uint64;\n" + " }\n" + " leaf counter142 {\n" + " type uint64;\n" + " }\n" + " leaf counter143 {\n" + " type uint64;\n" + " }\n" + " leaf counter144 {\n" + " type uint64;\n" + " }\n" + " leaf counter145 {\n" + " type uint64;\n" + " }\n" + " leaf counter146 {\n" + " type uint64;\n" + " }\n" + " leaf counter147 {\n" + " type uint64;\n" + " }\n" + " leaf counter148 {\n" + " type uint64;\n" + " }\n" + " leaf counter149 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter150 {\n" + " type uint64;\n" + " }\n" + " leaf counter151 {\n" + " type uint64;\n" + " }\n" + " leaf counter152 {\n" + " type uint64;\n" + " }\n" + " leaf counter153 {\n" + " type uint64;\n" + " }\n" + " leaf counter154 {\n" + " type uint64;\n" + " }\n" + " leaf counter155 {\n" + " type uint64;\n" + " }\n" + " leaf counter156 {\n" + " type uint64;\n" + " }\n" + " leaf counter157 {\n" + " type uint64;\n" + " }\n" + " leaf counter158 {\n" + " type uint64;\n" + " }\n" + " leaf counter159 {\n" + " type uint64;\n" + " }\n" + " leaf counter160 {\n" + " type uint64;\n" + " }\n" + " leaf counter161 {\n" + " type uint64;\n" + " }\n" + " leaf counter162 {\n" + " type uint64;\n" + " }\n" + " leaf counter163 {\n" + " type uint64;\n" + " }\n" + " leaf counter164 {\n" + " type uint64;\n" + " }\n" + " leaf counter165 {\n" + " type uint64;\n" + " }\n" + " leaf counter166 {\n" + " type uint64;\n" + " }\n" + " leaf counter167 {\n" + " type uint64;\n" + " }\n" + " leaf counter168 {\n" + " type uint64;\n" + " }\n" + " leaf counter169 {\n" + " type uint64;\n" + " }\n" + " leaf counter170 {\n" + " type uint64;\n" + " }\n" + " leaf counter171 {\n" + " type uint64;\n" + " }\n" + " leaf counter172 {\n" + " type uint64;\n" + " }\n" + " leaf counter173 {\n" + " type uint64;\n" + " }\n" + " leaf counter174 {\n" + " type uint64;\n" + " }\n" + " leaf counter175 {\n" + " type uint64;\n" + " }\n" + " leaf counter176 {\n" + " type uint64;\n" + " }\n" + " leaf counter177 {\n" + " type uint64;\n" + " }\n" + " leaf counter178 {\n" + " type uint64;\n" + " }\n" + " leaf counter179 {\n" + " type uint64;\n" + " }\n" + " leaf counter180 {\n" + " type uint64;\n" + " }\n" + " leaf counter181 {\n" + " type uint64;\n" + " }\n" + " leaf counter182 {\n" + " type uint64;\n" + " }\n" + " leaf counter183 {\n" + " type uint64;\n" + " }\n" + " leaf counter184 {\n" + " type uint64;\n" + " }\n" + " leaf counter185 {\n" + " type uint64;\n" + " }\n" + " leaf counter186 {\n" + " type uint64;\n" + " }\n" + " leaf counter187 {\n" + " type uint64;\n" + " }\n" + " leaf counter188 {\n" + " type uint64;\n" + " }\n" + " leaf counter189 {\n" + " type uint64;\n" + " }\n" + " leaf counter190 {\n" + " type uint64;\n" + " }\n" + " leaf counter191 {\n" + " type uint64;\n" + " }\n" + " leaf counter192 {\n" + " type uint64;\n" + " }\n" + " leaf counter193 {\n" + " type uint64;\n" + " }\n" + " leaf counter194 {\n" + " type uint64;\n" + " }\n" + " leaf counter195 {\n" + " type uint64;\n" + " }\n" + " leaf counter196 {\n" + " type uint64;\n" + " }\n" + " leaf counter197 {\n" + " type uint64;\n" + " }\n" + " leaf counter198 {\n" + " type uint64;\n" + " }\n" + " leaf counter199 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter200 {\n" + " type uint64;\n" + " }\n" + " leaf counter201 {\n" + " type uint64;\n" + " }\n" + " leaf counter202 {\n" + " type uint64;\n" + " }\n" + " leaf counter203 {\n" + " type uint64;\n" + " }\n" + " leaf counter204 {\n" + " type uint64;\n" + " }\n" + " leaf counter205 {\n" + " type uint64;\n" + " }\n" + " leaf counter206 {\n" + " type uint64;\n" + " }\n" + " leaf counter207 {\n" + " type uint64;\n" + " }\n" + " leaf counter208 {\n" + " type uint64;\n" + " }\n" + " leaf counter209 {\n" + " type uint64;\n" + " }\n" + " leaf counter210 {\n" + " type uint64;\n" + " }\n" + " leaf counter211 {\n" + " type uint64;\n" + " }\n" + " leaf counter212 {\n" + " type uint64;\n" + " }\n" + " leaf counter213 {\n" + " type uint64;\n" + " }\n" + " leaf counter214 {\n" + " type uint64;\n" + " }\n" + " leaf counter215 {\n" + " type uint64;\n" + " }\n" + " leaf counter216 {\n" + " type uint64;\n" + " }\n" + " leaf counter217 {\n" + " type uint64;\n" + " }\n" + " leaf counter218 {\n" + " type uint64;\n" + " }\n" + " leaf counter219 {\n" + " type uint64;\n" + " }\n" + " leaf counter220 {\n" + " type uint64;\n" + " }\n" + " leaf counter221 {\n" + " type uint64;\n" + " }\n" + " leaf counter222 {\n" + " type uint64;\n" + " }\n" + " leaf counter223 {\n" + " type uint64;\n" + " }\n" + " leaf counter224 {\n" + " type uint64;\n" + " }\n" + " leaf counter225 {\n" + " type uint64;\n" + " }\n" + " leaf counter226 {\n" + " type uint64;\n" + " }\n" + " leaf counter227 {\n" + " type uint64;\n" + " }\n" + " leaf counter228 {\n" + " type uint64;\n" + " }\n" + " leaf counter229 {\n" + " type uint64;\n" + " }\n" + " leaf counter230 {\n" + " type uint64;\n" + " }\n" + " leaf counter231 {\n" + " type uint64;\n" + " }\n" + " leaf counter232 {\n" + " type uint64;\n" + " }\n" + " leaf counter233 {\n" + " type uint64;\n" + " }\n" + " leaf counter234 {\n" + " type uint64;\n" + " }\n" + " leaf counter235 {\n" + " type uint64;\n" + " }\n" + " leaf counter236 {\n" + " type uint64;\n" + " }\n" + " leaf counter237 {\n" + " type uint64;\n" + " }\n" + " leaf counter238 {\n" + " type uint64;\n" + " }\n" + " leaf counter239 {\n" + " type uint64;\n" + " }\n" + " leaf counter240 {\n" + " type uint64;\n" + " }\n" + " leaf counter241 {\n" + " type uint64;\n" + " }\n" + " leaf counter242 {\n" + " type uint64;\n" + " }\n" + " leaf counter243 {\n" + " type uint64;\n" + " }\n" + " leaf counter244 {\n" + " type uint64;\n" + " }\n" + " leaf counter245 {\n" + " type uint64;\n" + " }\n" + " leaf counter246 {\n" + " type uint64;\n" + " }\n" + " leaf counter247 {\n" + " type uint64;\n" + " }\n" + " leaf counter248 {\n" + " type uint64;\n" + " }\n" + " leaf counter249 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter250 {\n" + " type uint64;\n" + " }\n" + " leaf counter251 {\n" + " type uint64;\n" + " }\n" + " leaf counter252 {\n" + " type uint64;\n" + " }\n" + " leaf counter253 {\n" + " type uint64;\n" + " }\n" + " leaf counter254 {\n" + " type uint64;\n" + " }\n" + " leaf counter255 {\n" + " type uint64;\n" + " }\n" + " leaf counter256 {\n" + " type uint64;\n" + " }\n" + " leaf counter257 {\n" + " type uint64;\n" + " }\n" + " leaf counter258 {\n" + " type uint64;\n" + " }\n" + " leaf counter259 {\n" + " type uint64;\n" + " }\n" + " leaf counter260 {\n" + " type uint64;\n" + " }\n" + " leaf counter261 {\n" + " type uint64;\n" + " }\n" + " leaf counter262 {\n" + " type uint64;\n" + " }\n" + " leaf counter263 {\n" + " type uint64;\n" + " }\n" + " leaf counter264 {\n" + " type uint64;\n" + " }\n" + " leaf counter265 {\n" + " type uint64;\n" + " }\n" + " leaf counter266 {\n" + " type uint64;\n" + " }\n" + " leaf counter267 {\n" + " type uint64;\n" + " }\n" + " leaf counter268 {\n" + " type uint64;\n" + " }\n" + " leaf counter269 {\n" + " type uint64;\n" + " }\n" + " leaf counter270 {\n" + " type uint64;\n" + " }\n" + " leaf counter271 {\n" + " type uint64;\n" + " }\n" + " leaf counter272 {\n" + " type uint64;\n" + " }\n" + " leaf counter273 {\n" + " type uint64;\n" + " }\n" + " leaf counter274 {\n" + " type uint64;\n" + " }\n" + " leaf counter275 {\n" + " type uint64;\n" + " }\n" + " leaf counter276 {\n" + " type uint64;\n" + " }\n" + " leaf counter277 {\n" + " type uint64;\n" + " }\n" + " leaf counter278 {\n" + " type uint64;\n" + " }\n" + " leaf counter279 {\n" + " type uint64;\n" + " }\n" + " leaf counter280 {\n" + " type uint64;\n" + " }\n" + " leaf counter281 {\n" + " type uint64;\n" + " }\n" + " leaf counter282 {\n" + " type uint64;\n" + " }\n" + " leaf counter283 {\n" + " type uint64;\n" + " }\n" + " leaf counter284 {\n" + " type uint64;\n" + " }\n" + " leaf counter285 {\n" + " type uint64;\n" + " }\n" + " leaf counter286 {\n" + " type uint64;\n" + " }\n" + " leaf counter287 {\n" + " type uint64;\n" + " }\n" + " leaf counter288 {\n" + " type uint64;\n" + " }\n" + " leaf counter289 {\n" + " type uint64;\n" + " }\n" + " leaf counter290 {\n" + " type uint64;\n" + " }\n" + " leaf counter291 {\n" + " type uint64;\n" + " }\n" + " leaf counter292 {\n" + " type uint64;\n" + " }\n" + " leaf counter293 {\n" + " type uint64;\n" + " }\n" + " leaf counter294 {\n" + " type uint64;\n" + " }\n" + " leaf counter295 {\n" + " type uint64;\n" + " }\n" + " leaf counter296 {\n" + " type uint64;\n" + " }\n" + " leaf counter297 {\n" + " type uint64;\n" + " }\n" + " leaf counter298 {\n" + " type uint64;\n" + " }\n" + " leaf counter299 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter300 {\n" + " type uint64;\n" + " }\n" + " leaf counter301 {\n" + " type uint64;\n" + " }\n" + " leaf counter302 {\n" + " type uint64;\n" + " }\n" + " leaf counter303 {\n" + " type uint64;\n" + " }\n" + " leaf counter304 {\n" + " type uint64;\n" + " }\n" + " leaf counter305 {\n" + " type uint64;\n" + " }\n" + " leaf counter306 {\n" + " type uint64;\n" + " }\n" + " leaf counter307 {\n" + " type uint64;\n" + " }\n" + " leaf counter308 {\n" + " type uint64;\n" + " }\n" + " leaf counter309 {\n" + " type uint64;\n" + " }\n" + " leaf counter310 {\n" + " type uint64;\n" + " }\n" + " leaf counter311 {\n" + " type uint64;\n" + " }\n" + " leaf counter312 {\n" + " type uint64;\n" + " }\n" + " leaf counter313 {\n" + " type uint64;\n" + " }\n" + " leaf counter314 {\n" + " type uint64;\n" + " }\n" + " leaf counter315 {\n" + " type uint64;\n" + " }\n" + " leaf counter316 {\n" + " type uint64;\n" + " }\n" + " leaf counter317 {\n" + " type uint64;\n" + " }\n" + " leaf counter318 {\n" + " type uint64;\n" + " }\n" + " leaf counter319 {\n" + " type uint64;\n" + " }\n" + " leaf counter320 {\n" + " type uint64;\n" + " }\n" + " leaf counter321 {\n" + " type uint64;\n" + " }\n" + " leaf counter322 {\n" + " type uint64;\n" + " }\n" + " leaf counter323 {\n" + " type uint64;\n" + " }\n" + " leaf counter324 {\n" + " type uint64;\n" + " }\n" + " leaf counter325 {\n" + " type uint64;\n" + " }\n" + " leaf counter326 {\n" + " type uint64;\n" + " }\n" + " leaf counter327 {\n" + " type uint64;\n" + " }\n" + " leaf counter328 {\n" + " type uint64;\n" + " }\n" + " leaf counter329 {\n" + " type uint64;\n" + " }\n" + " leaf counter330 {\n" + " type uint64;\n" + " }\n" + " leaf counter331 {\n" + " type uint64;\n" + " }\n" + " leaf counter332 {\n" + " type uint64;\n" + " }\n" + " leaf counter333 {\n" + " type uint64;\n" + " }\n" + " leaf counter334 {\n" + " type uint64;\n" + " }\n" + " leaf counter335 {\n" + " type uint64;\n" + " }\n" + " leaf counter336 {\n" + " type uint64;\n" + " }\n" + " leaf counter337 {\n" + " type uint64;\n" + " }\n" + " leaf counter338 {\n" + " type uint64;\n" + " }\n" + " leaf counter339 {\n" + " type uint64;\n" + " }\n" + " leaf counter340 {\n" + " type uint64;\n" + " }\n" + " leaf counter341 {\n" + " type uint64;\n" + " }\n" + " leaf counter342 {\n" + " type uint64;\n" + " }\n" + " leaf counter343 {\n" + " type uint64;\n" + " }\n" + " leaf counter344 {\n" + " type uint64;\n" + " }\n" + " leaf counter345 {\n" + " type uint64;\n" + " }\n" + " leaf counter346 {\n" + " type uint64;\n" + " }\n" + " leaf counter347 {\n" + " type uint64;\n" + " }\n" + " leaf counter348 {\n" + " type uint64;\n" + " }\n" + " leaf counter349 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter350 {\n" + " type uint64;\n" + " }\n" + " leaf counter351 {\n" + " type uint64;\n" + " }\n" + " leaf counter352 {\n" + " type uint64;\n" + " }\n" + " leaf counter353 {\n" + " type uint64;\n" + " }\n" + " leaf counter354 {\n" + " type uint64;\n" + " }\n" + " leaf counter355 {\n" + " type uint64;\n" + " }\n" + " leaf counter356 {\n" + " type uint64;\n" + " }\n" + " leaf counter357 {\n" + " type uint64;\n" + " }\n" + " leaf counter358 {\n" + " type uint64;\n" + " }\n" + " leaf counter359 {\n" + " type uint64;\n" + " }\n" + " leaf counter360 {\n" + " type uint64;\n" + " }\n" + " leaf counter361 {\n" + " type uint64;\n" + " }\n" + " leaf counter362 {\n" + " type uint64;\n" + " }\n" + " leaf counter363 {\n" + " type uint64;\n" + " }\n" + " leaf counter364 {\n" + " type uint64;\n" + " }\n" + " leaf counter365 {\n" + " type uint64;\n" + " }\n" + " leaf counter366 {\n" + " type uint64;\n" + " }\n" + " leaf counter367 {\n" + " type uint64;\n" + " }\n" + " leaf counter368 {\n" + " type uint64;\n" + " }\n" + " leaf counter369 {\n" + " type uint64;\n" + " }\n" + " leaf counter370 {\n" + " type uint64;\n" + " }\n" + " leaf counter371 {\n" + " type uint64;\n" + " }\n" + " leaf counter372 {\n" + " type uint64;\n" + " }\n" + " leaf counter373 {\n" + " type uint64;\n" + " }\n" + " leaf counter374 {\n" + " type uint64;\n" + " }\n" + " leaf counter375 {\n" + " type uint64;\n" + " }\n" + " leaf counter376 {\n" + " type uint64;\n" + " }\n" + " leaf counter377 {\n" + " type uint64;\n" + " }\n" + " leaf counter378 {\n" + " type uint64;\n" + " }\n" + " leaf counter379 {\n" + " type uint64;\n" + " }\n" + " leaf counter380 {\n" + " type uint64;\n" + " }\n" + " leaf counter381 {\n" + " type uint64;\n" + " }\n" + " leaf counter382 {\n" + " type uint64;\n" + " }\n" + " leaf counter383 {\n" + " type uint64;\n" + " }\n" + " leaf counter384 {\n" + " type uint64;\n" + " }\n" + " leaf counter385 {\n" + " type uint64;\n" + " }\n" + " leaf counter386 {\n" + " type uint64;\n" + " }\n" + " leaf counter387 {\n" + " type uint64;\n" + " }\n" + " leaf counter388 {\n" + " type uint64;\n" + " }\n" + " leaf counter389 {\n" + " type uint64;\n" + " }\n" + " leaf counter390 {\n" + " type uint64;\n" + " }\n" + " leaf counter391 {\n" + " type uint64;\n" + " }\n" + " leaf counter392 {\n" + " type uint64;\n" + " }\n" + " leaf counter393 {\n" + " type uint64;\n" + " }\n" + " leaf counter394 {\n" + " type uint64;\n" + " }\n" + " leaf counter395 {\n" + " type uint64;\n" + " }\n" + " leaf counter396 {\n" + " type uint64;\n" + " }\n" + " leaf counter397 {\n" + " type uint64;\n" + " }\n" + " leaf counter398 {\n" + " type uint64;\n" + " }\n" + " leaf counter399 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter400 {\n" + " type uint64;\n" + " }\n" + " leaf counter401 {\n" + " type uint64;\n" + " }\n" + " leaf counter402 {\n" + " type uint64;\n" + " }\n" + " leaf counter403 {\n" + " type uint64;\n" + " }\n" + " leaf counter404 {\n" + " type uint64;\n" + " }\n" + " leaf counter405 {\n" + " type uint64;\n" + " }\n" + " leaf counter406 {\n" + " type uint64;\n" + " }\n" + " leaf counter407 {\n" + " type uint64;\n" + " }\n" + " leaf counter408 {\n" + " type uint64;\n" + " }\n" + " leaf counter409 {\n" + " type uint64;\n" + " }\n" + " leaf counter410 {\n" + " type uint64;\n" + " }\n" + " leaf counter411 {\n" + " type uint64;\n" + " }\n" + " leaf counter412 {\n" + " type uint64;\n" + " }\n" + " leaf counter413 {\n" + " type uint64;\n" + " }\n" + " leaf counter414 {\n" + " type uint64;\n" + " }\n" + " leaf counter415 {\n" + " type uint64;\n" + " }\n" + " leaf counter416 {\n" + " type uint64;\n" + " }\n" + " leaf counter417 {\n" + " type uint64;\n" + " }\n" + " leaf counter418 {\n" + " type uint64;\n" + " }\n" + " leaf counter419 {\n" + " type uint64;\n" + " }\n" + " leaf counter420 {\n" + " type uint64;\n" + " }\n" + " leaf counter421 {\n" + " type uint64;\n" + " }\n" + " leaf counter422 {\n" + " type uint64;\n" + " }\n" + " leaf counter423 {\n" + " type uint64;\n" + " }\n" + " leaf counter424 {\n" + " type uint64;\n" + " }\n" + " leaf counter425 {\n" + " type uint64;\n" + " }\n" + " leaf counter426 {\n" + " type uint64;\n" + " }\n" + " leaf counter427 {\n" + " type uint64;\n" + " }\n" + " leaf counter428 {\n" + " type uint64;\n" + " }\n" + " leaf counter429 {\n" + " type uint64;\n" + " }\n" + " leaf counter430 {\n" + " type uint64;\n" + " }\n" + " leaf counter431 {\n" + " type uint64;\n" + " }\n" + " leaf counter432 {\n" + " type uint64;\n" + " }\n" + " leaf counter433 {\n" + " type uint64;\n" + " }\n" + " leaf counter434 {\n" + " type uint64;\n" + " }\n" + " leaf counter435 {\n" + " type uint64;\n" + " }\n" + " leaf counter436 {\n" + " type uint64;\n" + " }\n" + " leaf counter437 {\n" + " type uint64;\n" + " }\n" + " leaf counter438 {\n" + " type uint64;\n" + " }\n" + " leaf counter439 {\n" + " type uint64;\n" + " }\n" + " leaf counter440 {\n" + " type uint64;\n" + " }\n" + " leaf counter441 {\n" + " type uint64;\n" + " }\n" + " leaf counter442 {\n" + " type uint64;\n" + " }\n" + " leaf counter443 {\n" + " type uint64;\n" + " }\n" + " leaf counter444 {\n" + " type uint64;\n" + " }\n" + " leaf counter445 {\n" + " type uint64;\n" + " }\n" + " leaf counter446 {\n" + " type uint64;\n" + " }\n" + " leaf counter447 {\n" + " type uint64;\n" + " }\n" + " leaf counter448 {\n" + " type uint64;\n" + " }\n" + " leaf counter449 {\n" + " type uint64;\n" + " }\n"); + strcat(counters_yang, + " leaf counter450 {\n" + " type uint64;\n" + " }\n" + " leaf counter451 {\n" + " type uint64;\n" + " }\n" + " leaf counter452 {\n" + " type uint64;\n" + " }\n" + " leaf counter453 {\n" + " type uint64;\n" + " }\n" + " leaf counter454 {\n" + " type uint64;\n" + " }\n" + " leaf counter455 {\n" + " type uint64;\n" + " }\n" + " leaf counter456 {\n" + " type uint64;\n" + " }\n" + " leaf counter457 {\n" + " type uint64;\n" + " }\n" + " leaf counter458 {\n" + " type uint64;\n" + " }\n" + " leaf counter459 {\n" + " type uint64;\n" + " }\n" + " leaf counter460 {\n" + " type uint64;\n" + " }\n" + " leaf counter461 {\n" + " type uint64;\n" + " }\n" + " leaf counter462 {\n" + " type uint64;\n" + " }\n" + " leaf counter463 {\n" + " type uint64;\n" + " }\n" + " leaf counter464 {\n" + " type uint64;\n" + " }\n" + " leaf counter465 {\n" + " type uint64;\n" + " }\n" + " leaf counter466 {\n" + " type uint64;\n" + " }\n" + " leaf counter467 {\n" + " type uint64;\n" + " }\n" + " leaf counter468 {\n" + " type uint64;\n" + " }\n" + " leaf counter469 {\n" + " type uint64;\n" + " }\n" + " leaf counter470 {\n" + " type uint64;\n" + " }\n" + " leaf counter471 {\n" + " type uint64;\n" + " }\n" + " leaf counter472 {\n" + " type uint64;\n" + " }\n" + " leaf counter473 {\n" + " type uint64;\n" + " }\n" + " leaf counter474 {\n" + " type uint64;\n" + " }\n" + " leaf counter475 {\n" + " type uint64;\n" + " }\n" + " leaf counter476 {\n" + " type uint64;\n" + " }\n" + " leaf counter477 {\n" + " type uint64;\n" + " }\n" + " leaf counter478 {\n" + " type uint64;\n" + " }\n" + " leaf counter479 {\n" + " type uint64;\n" + " }\n" + " leaf counter480 {\n" + " type uint64;\n" + " }\n" + " leaf counter481 {\n" + " type uint64;\n" + " }\n" + " leaf counter482 {\n" + " type uint64;\n" + " }\n" + " leaf counter483 {\n" + " type uint64;\n" + " }\n" + " leaf counter484 {\n" + " type uint64;\n" + " }\n" + " leaf counter485 {\n" + " type uint64;\n" + " }\n" + " leaf counter486 {\n" + " type uint64;\n" + " }\n" + " leaf counter487 {\n" + " type uint64;\n" + " }\n" + " leaf counter488 {\n" + " type uint64;\n" + " }\n" + " leaf counter489 {\n" + " type uint64;\n" + " }\n" + " leaf counter490 {\n" + " type uint64;\n" + " }\n" + " leaf counter491 {\n" + " type uint64;\n" + " }\n" + " leaf counter492 {\n" + " type uint64;\n" + " }\n" + " leaf counter493 {\n" + " type uint64;\n" + " }\n" + " leaf counter494 {\n" + " type uint64;\n" + " }\n" + " leaf counter495 {\n" + " type uint64;\n" + " }\n" + " leaf counter496 {\n" + " type uint64;\n" + " }\n" + " leaf counter497 {\n" + " type uint64;\n" + " }\n" + " leaf counter498 {\n" + " type uint64;\n" + " }\n" + " leaf counter499 {\n" + " type uint64;\n" + " }\n" + " }\n" + "}\n"); + + data_xml = malloc(16384); + strcpy(data_xml, + "<stats xmlns=\"urn:counters\">\n"); + strcat(data_xml, + " <counter1>1</counter1>\n" + " <counter2>2</counter2>\n" + " <counter3>3</counter3>\n" + " <counter4>4</counter4>\n" + " <counter5>5</counter5>\n" + " <counter6>6</counter6>\n" + " <counter7>7</counter7>\n" + " <counter8>8</counter8>\n" + " <counter9>9</counter9>\n" + " <counter10>10</counter10>\n" + " <counter11>11</counter11>\n" + " <counter12>12</counter12>\n" + " <counter13>13</counter13>\n" + " <counter14>14</counter14>\n" + " <counter15>15</counter15>\n" + " <counter16>16</counter16>\n" + " <counter17>17</counter17>\n" + " <counter18>18</counter18>\n" + " <counter19>19</counter19>\n" + " <counter20>20</counter20>\n" + " <counter21>21</counter21>\n" + " <counter22>22</counter22>\n" + " <counter23>23</counter23>\n" + " <counter24>24</counter24>\n" + " <counter25>25</counter25>\n" + " <counter26>26</counter26>\n" + " <counter27>27</counter27>\n" + " <counter28>28</counter28>\n" + " <counter29>29</counter29>\n" + " <counter30>30</counter30>\n" + " <counter31>31</counter31>\n" + " <counter32>32</counter32>\n" + " <counter33>33</counter33>\n" + " <counter34>34</counter34>\n" + " <counter35>35</counter35>\n" + " <counter36>36</counter36>\n" + " <counter37>37</counter37>\n" + " <counter38>38</counter38>\n" + " <counter39>39</counter39>\n" + " <counter40>40</counter40>\n" + " <counter41>41</counter41>\n" + " <counter42>42</counter42>\n" + " <counter43>43</counter43>\n" + " <counter44>44</counter44>\n" + " <counter45>45</counter45>\n" + " <counter46>46</counter46>\n" + " <counter47>47</counter47>\n" + " <counter48>48</counter48>\n" + " <counter49>49</counter49>\n" + " <counter50>50</counter50>\n" + " <counter51>51</counter51>\n" + " <counter52>52</counter52>\n" + " <counter53>53</counter53>\n" + " <counter54>54</counter54>\n" + " <counter55>55</counter55>\n" + " <counter56>56</counter56>\n" + " <counter57>57</counter57>\n" + " <counter58>58</counter58>\n" + " <counter59>59</counter59>\n" + " <counter60>60</counter60>\n" + " <counter61>61</counter61>\n" + " <counter62>62</counter62>\n" + " <counter63>63</counter63>\n" + " <counter64>64</counter64>\n" + " <counter65>65</counter65>\n" + " <counter66>66</counter66>\n" + " <counter67>67</counter67>\n" + " <counter68>68</counter68>\n" + " <counter69>69</counter69>\n" + " <counter70>70</counter70>\n" + " <counter71>71</counter71>\n" + " <counter72>72</counter72>\n" + " <counter73>73</counter73>\n" + " <counter74>74</counter74>\n" + " <counter75>75</counter75>\n" + " <counter76>76</counter76>\n" + " <counter77>77</counter77>\n" + " <counter78>78</counter78>\n" + " <counter79>79</counter79>\n" + " <counter80>80</counter80>\n" + " <counter81>81</counter81>\n" + " <counter82>82</counter82>\n" + " <counter83>83</counter83>\n" + " <counter84>84</counter84>\n" + " <counter85>85</counter85>\n" + " <counter86>86</counter86>\n" + " <counter87>87</counter87>\n" + " <counter88>88</counter88>\n" + " <counter89>89</counter89>\n" + " <counter90>90</counter90>\n" + " <counter91>91</counter91>\n" + " <counter92>92</counter92>\n" + " <counter93>93</counter93>\n" + " <counter94>94</counter94>\n" + " <counter95>95</counter95>\n" + " <counter96>96</counter96>\n" + " <counter97>97</counter97>\n" + " <counter98>98</counter98>\n" + " <counter99>99</counter99>\n"); + strcat(data_xml, + " <counter100>100</counter100>\n" + " <counter101>101</counter101>\n" + " <counter102>102</counter102>\n" + " <counter103>103</counter103>\n" + " <counter104>104</counter104>\n" + " <counter105>105</counter105>\n" + " <counter106>106</counter106>\n" + " <counter107>107</counter107>\n" + " <counter108>108</counter108>\n" + " <counter109>109</counter109>\n" + " <counter110>110</counter110>\n" + " <counter111>111</counter111>\n" + " <counter112>112</counter112>\n" + " <counter113>113</counter113>\n" + " <counter114>114</counter114>\n" + " <counter115>115</counter115>\n" + " <counter116>116</counter116>\n" + " <counter117>117</counter117>\n" + " <counter118>118</counter118>\n" + " <counter119>119</counter119>\n" + " <counter120>120</counter120>\n" + " <counter121>121</counter121>\n" + " <counter122>122</counter122>\n" + " <counter123>123</counter123>\n" + " <counter124>124</counter124>\n" + " <counter125>125</counter125>\n" + " <counter126>126</counter126>\n" + " <counter127>127</counter127>\n" + " <counter128>128</counter128>\n" + " <counter129>129</counter129>\n" + " <counter130>130</counter130>\n" + " <counter131>131</counter131>\n" + " <counter132>132</counter132>\n" + " <counter133>133</counter133>\n" + " <counter134>134</counter134>\n" + " <counter135>135</counter135>\n" + " <counter136>136</counter136>\n" + " <counter137>137</counter137>\n" + " <counter138>138</counter138>\n" + " <counter139>139</counter139>\n" + " <counter140>140</counter140>\n" + " <counter141>141</counter141>\n" + " <counter142>142</counter142>\n" + " <counter143>143</counter143>\n" + " <counter144>144</counter144>\n" + " <counter145>145</counter145>\n" + " <counter146>146</counter146>\n" + " <counter147>147</counter147>\n" + " <counter148>148</counter148>\n" + " <counter149>149</counter149>\n" + " <counter150>150</counter150>\n" + " <counter151>151</counter151>\n" + " <counter152>152</counter152>\n" + " <counter153>153</counter153>\n" + " <counter154>154</counter154>\n" + " <counter155>155</counter155>\n" + " <counter156>156</counter156>\n" + " <counter157>157</counter157>\n" + " <counter158>158</counter158>\n" + " <counter159>159</counter159>\n" + " <counter160>160</counter160>\n" + " <counter161>161</counter161>\n" + " <counter162>162</counter162>\n" + " <counter163>163</counter163>\n" + " <counter164>164</counter164>\n" + " <counter165>165</counter165>\n" + " <counter166>166</counter166>\n" + " <counter167>167</counter167>\n" + " <counter168>168</counter168>\n" + " <counter169>169</counter169>\n" + " <counter170>170</counter170>\n" + " <counter171>171</counter171>\n" + " <counter172>172</counter172>\n" + " <counter173>173</counter173>\n" + " <counter174>174</counter174>\n" + " <counter175>175</counter175>\n" + " <counter176>176</counter176>\n" + " <counter177>177</counter177>\n" + " <counter178>178</counter178>\n" + " <counter179>179</counter179>\n" + " <counter180>180</counter180>\n" + " <counter181>181</counter181>\n" + " <counter182>182</counter182>\n" + " <counter183>183</counter183>\n" + " <counter184>184</counter184>\n" + " <counter185>185</counter185>\n" + " <counter186>186</counter186>\n" + " <counter187>187</counter187>\n" + " <counter188>188</counter188>\n" + " <counter189>189</counter189>\n" + " <counter190>190</counter190>\n" + " <counter191>191</counter191>\n" + " <counter192>192</counter192>\n" + " <counter193>193</counter193>\n" + " <counter194>194</counter194>\n" + " <counter195>195</counter195>\n" + " <counter196>196</counter196>\n" + " <counter197>197</counter197>\n" + " <counter198>198</counter198>\n" + " <counter199>199</counter199>\n"); + strcat(data_xml, + " <counter200>200</counter200>\n" + " <counter201>201</counter201>\n" + " <counter202>202</counter202>\n" + " <counter203>203</counter203>\n" + " <counter204>204</counter204>\n" + " <counter205>205</counter205>\n" + " <counter206>206</counter206>\n" + " <counter207>207</counter207>\n" + " <counter208>208</counter208>\n" + " <counter209>209</counter209>\n" + " <counter210>210</counter210>\n" + " <counter211>211</counter211>\n" + " <counter212>212</counter212>\n" + " <counter213>213</counter213>\n" + " <counter214>214</counter214>\n" + " <counter215>215</counter215>\n" + " <counter216>216</counter216>\n" + " <counter217>217</counter217>\n" + " <counter218>218</counter218>\n" + " <counter219>219</counter219>\n" + " <counter220>220</counter220>\n" + " <counter221>221</counter221>\n" + " <counter222>222</counter222>\n" + " <counter223>223</counter223>\n" + " <counter224>224</counter224>\n" + " <counter225>225</counter225>\n" + " <counter226>226</counter226>\n" + " <counter227>227</counter227>\n" + " <counter228>228</counter228>\n" + " <counter229>229</counter229>\n" + " <counter230>230</counter230>\n" + " <counter231>231</counter231>\n" + " <counter232>232</counter232>\n" + " <counter233>233</counter233>\n" + " <counter234>234</counter234>\n" + " <counter235>235</counter235>\n" + " <counter236>236</counter236>\n" + " <counter237>237</counter237>\n" + " <counter238>238</counter238>\n" + " <counter239>239</counter239>\n" + " <counter240>240</counter240>\n" + " <counter241>241</counter241>\n" + " <counter242>242</counter242>\n" + " <counter243>243</counter243>\n" + " <counter244>244</counter244>\n" + " <counter245>245</counter245>\n" + " <counter246>246</counter246>\n" + " <counter247>247</counter247>\n" + " <counter248>248</counter248>\n" + " <counter249>249</counter249>\n" + " <counter250>250</counter250>\n" + " <counter251>251</counter251>\n" + " <counter252>252</counter252>\n" + " <counter253>253</counter253>\n" + " <counter254>254</counter254>\n" + " <counter255>255</counter255>\n" + " <counter256>256</counter256>\n" + " <counter257>257</counter257>\n" + " <counter258>258</counter258>\n" + " <counter259>259</counter259>\n" + " <counter260>260</counter260>\n" + " <counter261>261</counter261>\n" + " <counter262>262</counter262>\n" + " <counter263>263</counter263>\n" + " <counter264>264</counter264>\n" + " <counter265>265</counter265>\n" + " <counter266>266</counter266>\n" + " <counter267>267</counter267>\n" + " <counter268>268</counter268>\n" + " <counter269>269</counter269>\n" + " <counter270>270</counter270>\n" + " <counter271>271</counter271>\n" + " <counter272>272</counter272>\n" + " <counter273>273</counter273>\n" + " <counter274>274</counter274>\n" + " <counter275>275</counter275>\n" + " <counter276>276</counter276>\n" + " <counter277>277</counter277>\n" + " <counter278>278</counter278>\n" + " <counter279>279</counter279>\n" + " <counter280>280</counter280>\n" + " <counter281>281</counter281>\n" + " <counter282>282</counter282>\n" + " <counter283>283</counter283>\n" + " <counter284>284</counter284>\n" + " <counter285>285</counter285>\n" + " <counter286>286</counter286>\n" + " <counter287>287</counter287>\n" + " <counter288>288</counter288>\n" + " <counter289>289</counter289>\n" + " <counter290>290</counter290>\n" + " <counter291>291</counter291>\n" + " <counter292>292</counter292>\n" + " <counter293>293</counter293>\n" + " <counter294>294</counter294>\n" + " <counter295>295</counter295>\n" + " <counter296>296</counter296>\n" + " <counter297>297</counter297>\n" + " <counter298>298</counter298>\n" + " <counter299>299</counter299>\n"); + strcat(data_xml, + " <counter300>300</counter300>\n" + " <counter301>301</counter301>\n" + " <counter302>302</counter302>\n" + " <counter303>303</counter303>\n" + " <counter304>304</counter304>\n" + " <counter305>305</counter305>\n" + " <counter306>306</counter306>\n" + " <counter307>307</counter307>\n" + " <counter308>308</counter308>\n" + " <counter309>309</counter309>\n" + " <counter310>310</counter310>\n" + " <counter311>311</counter311>\n" + " <counter312>312</counter312>\n" + " <counter313>313</counter313>\n" + " <counter314>314</counter314>\n" + " <counter315>315</counter315>\n" + " <counter316>316</counter316>\n" + " <counter317>317</counter317>\n" + " <counter318>318</counter318>\n" + " <counter319>319</counter319>\n" + " <counter320>320</counter320>\n" + " <counter321>321</counter321>\n" + " <counter322>322</counter322>\n" + " <counter323>323</counter323>\n" + " <counter324>324</counter324>\n" + " <counter325>325</counter325>\n" + " <counter326>326</counter326>\n" + " <counter327>327</counter327>\n" + " <counter328>328</counter328>\n" + " <counter329>329</counter329>\n" + " <counter330>330</counter330>\n" + " <counter331>331</counter331>\n" + " <counter332>332</counter332>\n" + " <counter333>333</counter333>\n" + " <counter334>334</counter334>\n" + " <counter335>335</counter335>\n" + " <counter336>336</counter336>\n" + " <counter337>337</counter337>\n" + " <counter338>338</counter338>\n" + " <counter339>339</counter339>\n" + " <counter340>340</counter340>\n" + " <counter341>341</counter341>\n" + " <counter342>342</counter342>\n" + " <counter343>343</counter343>\n" + " <counter344>344</counter344>\n" + " <counter345>345</counter345>\n" + " <counter346>346</counter346>\n" + " <counter347>347</counter347>\n" + " <counter348>348</counter348>\n" + " <counter349>349</counter349>\n" + " <counter350>350</counter350>\n" + " <counter351>351</counter351>\n" + " <counter352>352</counter352>\n" + " <counter353>353</counter353>\n" + " <counter354>354</counter354>\n" + " <counter355>355</counter355>\n" + " <counter356>356</counter356>\n" + " <counter357>357</counter357>\n" + " <counter358>358</counter358>\n" + " <counter359>359</counter359>\n" + " <counter360>360</counter360>\n" + " <counter361>361</counter361>\n" + " <counter362>362</counter362>\n" + " <counter363>363</counter363>\n" + " <counter364>364</counter364>\n" + " <counter365>365</counter365>\n" + " <counter366>366</counter366>\n" + " <counter367>367</counter367>\n" + " <counter368>368</counter368>\n" + " <counter369>369</counter369>\n" + " <counter370>370</counter370>\n" + " <counter371>371</counter371>\n" + " <counter372>372</counter372>\n" + " <counter373>373</counter373>\n" + " <counter374>374</counter374>\n" + " <counter375>375</counter375>\n" + " <counter376>376</counter376>\n" + " <counter377>377</counter377>\n" + " <counter378>378</counter378>\n" + " <counter379>379</counter379>\n" + " <counter380>380</counter380>\n" + " <counter381>381</counter381>\n" + " <counter382>382</counter382>\n" + " <counter383>383</counter383>\n" + " <counter384>384</counter384>\n" + " <counter385>385</counter385>\n" + " <counter386>386</counter386>\n" + " <counter387>387</counter387>\n" + " <counter388>388</counter388>\n" + " <counter389>389</counter389>\n" + " <counter390>390</counter390>\n" + " <counter391>391</counter391>\n" + " <counter392>392</counter392>\n" + " <counter393>393</counter393>\n" + " <counter394>394</counter394>\n" + " <counter395>395</counter395>\n" + " <counter396>396</counter396>\n" + " <counter397>397</counter397>\n" + " <counter398>398</counter398>\n" + " <counter399>399</counter399>\n"); + strcat(data_xml, + " <counter400>400</counter400>\n" + " <counter401>401</counter401>\n" + " <counter402>402</counter402>\n" + " <counter403>403</counter403>\n" + " <counter404>404</counter404>\n" + " <counter405>405</counter405>\n" + " <counter406>406</counter406>\n" + " <counter407>407</counter407>\n" + " <counter408>408</counter408>\n" + " <counter409>409</counter409>\n" + " <counter410>410</counter410>\n" + " <counter411>411</counter411>\n" + " <counter412>412</counter412>\n" + " <counter413>413</counter413>\n" + " <counter414>414</counter414>\n" + " <counter415>415</counter415>\n" + " <counter416>416</counter416>\n" + " <counter417>417</counter417>\n" + " <counter418>418</counter418>\n" + " <counter419>419</counter419>\n" + " <counter420>420</counter420>\n" + " <counter421>421</counter421>\n" + " <counter422>422</counter422>\n" + " <counter423>423</counter423>\n" + " <counter424>424</counter424>\n" + " <counter425>425</counter425>\n" + " <counter426>426</counter426>\n" + " <counter427>427</counter427>\n" + " <counter428>428</counter428>\n" + " <counter429>429</counter429>\n" + " <counter430>430</counter430>\n" + " <counter431>431</counter431>\n" + " <counter432>432</counter432>\n" + " <counter433>433</counter433>\n" + " <counter434>434</counter434>\n" + " <counter435>435</counter435>\n" + " <counter436>436</counter436>\n" + " <counter437>437</counter437>\n" + " <counter438>438</counter438>\n" + " <counter439>439</counter439>\n" + " <counter440>440</counter440>\n" + " <counter441>441</counter441>\n" + " <counter442>442</counter442>\n" + " <counter443>443</counter443>\n" + " <counter444>444</counter444>\n" + " <counter445>445</counter445>\n" + " <counter446>446</counter446>\n" + " <counter447>447</counter447>\n" + " <counter448>448</counter448>\n" + " <counter449>449</counter449>\n" + " <counter450>450</counter450>\n" + " <counter451>451</counter451>\n" + " <counter452>452</counter452>\n" + " <counter453>453</counter453>\n" + " <counter454>454</counter454>\n" + " <counter455>455</counter455>\n" + " <counter456>456</counter456>\n" + " <counter457>457</counter457>\n" + " <counter458>458</counter458>\n" + " <counter459>459</counter459>\n" + " <counter460>460</counter460>\n" + " <counter461>461</counter461>\n" + " <counter462>462</counter462>\n" + " <counter463>463</counter463>\n" + " <counter464>464</counter464>\n" + " <counter465>465</counter465>\n" + " <counter466>466</counter466>\n" + " <counter467>467</counter467>\n" + " <counter468>468</counter468>\n" + " <counter469>469</counter469>\n" + " <counter470>470</counter470>\n" + " <counter471>471</counter471>\n" + " <counter472>472</counter472>\n" + " <counter473>473</counter473>\n" + " <counter474>474</counter474>\n" + " <counter475>475</counter475>\n" + " <counter476>476</counter476>\n" + " <counter477>477</counter477>\n" + " <counter478>478</counter478>\n" + " <counter479>479</counter479>\n" + " <counter480>480</counter480>\n" + " <counter481>481</counter481>\n" + " <counter482>482</counter482>\n" + " <counter483>483</counter483>\n" + " <counter484>484</counter484>\n" + " <counter485>485</counter485>\n" + " <counter486>486</counter486>\n" + " <counter487>487</counter487>\n" + " <counter488>488</counter488>\n" + " <counter489>489</counter489>\n" + " <counter490>490</counter490>\n" + " <counter491>491</counter491>\n" + " <counter492>492</counter492>\n" + " <counter493>493</counter493>\n" + " <counter494>494</counter494>\n" + " <counter495>495</counter495>\n" + " <counter496>496</counter496>\n" + " <counter497>497</counter497>\n" + " <counter498>498</counter498>\n" + " <counter499>499</counter499>\n" + "</stats>\n"); + + UTEST_ADD_MODULE(counters_yang, LYS_IN_YANG, NULL, NULL); + + check_print_parse(state, data_xml); + + free(counters_yang); + free(data_xml); +} + +#if 0 + +static void +test_types(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "types", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/types.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_annotations(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/annotations.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_similar_annot_names(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/similar-annot-names.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_many_child_annot(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/many-childs-annot.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_union(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "union", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_union2(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "statements", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/union2.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_collisions(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "annotations", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/collisions.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_anydata(void **state) +{ + struct state *st = (*state); + const struct lys_module *mod; + int ret; + const char *test_anydata = + "module test-anydata {" + " namespace \"urn:test-anydata\";" + " prefix ya;" + "" + " container cont {" + " anydata ntf;" + " }" + "}"; + + assert_non_null(ly_ctx_load_module(st->ctx, "ietf-netconf-notifications", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL); + assert_ptr_not_equal(st->dt1, NULL); + + / *get notification in LYB format to set as anydata content * / + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + lyd_free_withsiblings(st->dt1); + st->dt1 = NULL; + + / *now comes the real test, test anydata * / + mod = lys_parse_mem(st->ctx, test_anydata, LYS_YANG); + assert_non_null(mod); + + st->dt1 = lyd_new(NULL, mod, "cont"); + assert_non_null(st->dt1); + + assert_non_null(lyd_new_anydata(st->dt1, NULL, "ntf", st->mem, LYD_ANYDATA_LYBD)); + st->mem = NULL; + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + ret = lyd_validate(&st->dt1, LYD_OPT_CONFIG, NULL); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); + + /* and also test the embedded notification itself */ + free(st->mem); + ret = lyd_lyb_data_length(((struct lyd_node_anydata *)st->dt1->child)->value.mem); + st->mem = malloc(ret); + memcpy(st->mem, ((struct lyd_node_anydata *)st->dt1->child)->value.mem, ret); + + lyd_free_withsiblings(st->dt2); + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_NOTIF | LYD_OPT_STRICT | LYD_OPT_NOEXTDEPS, NULL); + assert_ptr_not_equal(st->dt2, NULL); + + /* parse the JSON again for this comparison */ + lyd_free_withsiblings(st->dt1); + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/ietf-netconf-notifications.json", LYD_JSON, LYD_OPT_NOTIF | LYD_OPT_TRUSTED, NULL); + assert_ptr_not_equal(st->dt1, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_submodule_feature(void **state) +{ + struct state *st = (*state); + const struct lys_module *mod; + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + mod = ly_ctx_load_module(st->ctx, "feature-submodule-main", NULL); + assert_non_null(mod); + assert_int_equal(lys_features_enable(mod, "test-submodule-feature"), 0); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/test-submodule-feature.json", LYD_JSON, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_coliding_augments(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "augment-target", NULL)); + assert_non_null(ly_ctx_load_module(st->ctx, "augment0", NULL)); + assert_non_null(ly_ctx_load_module(st->ctx, "augment1", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/augment.xml", LYD_XML, LYD_OPT_CONFIG); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +static void +test_leafrefs(void **state) +{ + struct state *st = (*state); + int ret; + + ly_ctx_set_searchdir(st->ctx, TESTS_DIR "/data/files"); + assert_non_null(ly_ctx_load_module(st->ctx, "leafrefs2", NULL)); + + st->dt1 = lyd_parse_path(st->ctx, TESTS_DIR "/data/files/leafrefs2.json", LYD_JSON, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt1, NULL); + + ret = lyd_print_mem(&st->mem, st->dt1, LYD_LYB, LYP_WITHSIBLINGS); + assert_int_equal(ret, 0); + + st->dt2 = lyd_parse_mem(st->ctx, st->mem, LYD_LYB, LYD_OPT_CONFIG | LYD_OPT_STRICT); + assert_ptr_not_equal(st->dt2, NULL); + + check_data_tree(st->dt1, st->dt2); +} + +#endif + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(tests_leaflist), + UTEST(tests_list), + UTEST(tests_any), + UTEST(test_ietf_interfaces, setup), + UTEST(test_origin, setup), + UTEST(test_statements, setup), + UTEST(test_opaq, setup), + UTEST(test_collisions, setup), +#if 0 + cmocka_unit_test_setup_teardown(test_types, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_annotations, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_similar_annot_names, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_many_child_annot, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_union, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_union2, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_collisions, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_anydata, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_submodule_feature, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_coliding_augments, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_leafrefs, setup_f, teardown_f), +#endif + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_merge.c b/tests/utests/data/test_merge.c new file mode 100644 index 0000000..3e7b772 --- /dev/null +++ b/tests/utests/data/test_merge.c @@ -0,0 +1,756 @@ +/** + * @file test_merge.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief tests for complex data merges. + * + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "libyang.h" + +#define LYD_TREE_CREATE(INPUT, MODEL) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL) + +#define CONTEXT_CREATE \ + CONTEXT_CREATE_PATH(NULL) + +#define LYD_TREE_CHECK_CHAR(MODEL, TEXT, PARAMS) \ + CHECK_LYD_STRING_PARAM(MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS | PARAMS) + +static void +test_batch(void **state) +{ + const char *start = + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2016-02-11</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n"; + const char *data[] = { + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2016-02-01</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2012-02-22</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-monitoring</name>\n" + " <revision>2010-10-04</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-with-defaults</name>\n" + " <revision>2011-06-01</revision>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2016-02-11</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2016-02-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2012-02-22</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>xpath</feature>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-monitoring</name>\n" + " <revision>2010-10-04</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n", + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>ietf-netconf-with-defaults</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n" + }; + const char *output_template = + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" + " <module>\n" + " <name>yang</name>\n" + " <revision>2016-02-11</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:1</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-yang-library</name>\n" + " <revision>2016-02-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-acm</name>\n" + " <revision>2012-02-22</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-acm</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:netconf:base:1.0</namespace>\n" + " <feature>writable-running</feature>\n" + " <feature>candidate</feature>\n" + " <feature>rollback-on-error</feature>\n" + " <feature>validate</feature>\n" + " <feature>startup</feature>\n" + " <feature>xpath</feature>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-monitoring</name>\n" + " <revision>2010-10-04</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + " <module>\n" + " <name>ietf-netconf-with-defaults</name>\n" + " <revision>2011-06-01</revision>\n" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults</namespace>\n" + " <conformance-type>implement</conformance-type>\n" + " </module>\n" + "</modules-state>\n"; + + struct lyd_node *target; + + CHECK_PARSE_LYD_PARAM(start, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, target); + + for (int32_t i = 0; i < 11; ++i) { + struct lyd_node *source; + + CHECK_PARSE_LYD_PARAM(data[i], LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, source); + assert_int_equal(LY_SUCCESS, lyd_merge_siblings(&target, source, LYD_MERGE_DESTRUCT)); + } + + LYD_TREE_CHECK_CHAR(target, output_template, 0); + + lyd_free_all(target); +} + +static void +test_leaf(void **state) +{ + const char *sch = "module x {" + " namespace urn:x;" + " prefix x;" + " container A {" + " leaf f1 {type string;}" + " container B {" + " leaf f2 {type string;}" + " }" + " }" + " }"; + const char *trg = "<A xmlns=\"urn:x\"> <f1>block</f1> </A>"; + const char *src = "<A xmlns=\"urn:x\"> <f1>aa</f1> <B> <f2>bb</f2> </B> </A>"; + const char *result = "<A xmlns=\"urn:x\"><f1>aa</f1><B><f2>bb</f2></B></A>"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, LYD_PRINT_SHRINK); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_container(void **state) +{ + const char *sch = + "module A {\n" + " namespace \"aa:A\";\n" + " prefix A;\n" + " container A {\n" + " leaf f1 {type string;}\n" + " container B {\n" + " leaf f2 {type string;}\n" + " }\n" + " container C {\n" + " leaf f3 {type string;}\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = "<A xmlns=\"aa:A\"> <B> <f2>aaa</f2> </B> </A>"; + const char *src = "<A xmlns=\"aa:A\"> <C> <f3>bbb</f3> </C> </A>"; + const char *result = "<A xmlns=\"aa:A\"><B><f2>aaa</f2></B><C><f3>bbb</f3></C></A>"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, LYD_PRINT_SHRINK); + + /* destroy */ + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_list(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " list b-list1 {\n" + " key p1;\n" + " leaf p1 {\n" + " type uint8;\n" + " }\n" + " leaf p2 {\n" + " type string;\n" + " }\n" + " leaf p3 {\n" + " type boolean;\n" + " default false;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <p3>true</p3>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " <p3>true</p3>\n" + " </b-list1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_list2(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " list b-list1 {\n" + " key p1;\n" + " leaf p1 {\n" + " type uint8;\n" + " }\n" + " leaf p2 {\n" + " type string;\n" + " }\n" + " container inner2 {\n" + " leaf p3 {\n" + " type boolean;\n" + " default false;\n" + " }\n" + " leaf p4 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_dup_inst_list(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " config false;\n" + " list b-list1 {\n" + " leaf p1 {\n" + " type uint8;\n" + " }\n" + " leaf p2 {\n" + " type string;\n" + " }\n" + " container inner2 {\n" + " leaf p4 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>2</p1>\n" + " <p2>a</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>b</p2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>1</p1>\n" + " <p2>a</p2>\n" + " <inner2>\n" + " <p4>val</p4>\n" + " </inner2>\n" + " </b-list1>\n" + " <b-list1>\n" + " <p1>2</p1>\n" + " <p2>a</p2>\n" + " </b-list1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_dup_inst_llist(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + "\n" + " container inner1 {\n" + " config false;\n" + " leaf-list b-llist1 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + "</inner1>\n"; + const char *src = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + "</inner1>\n"; + const char *result = + "<inner1 xmlns=\"http://test/merge\">\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>b</b-llist1>\n" + " <b-llist1>c</b-llist1>\n" + " <b-llist1>d</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>a</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + " <b-llist1>f</b-llist1>\n" + "</inner1>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_case(void **state) +{ + const char *sch = + "module merge {\n" + " namespace \"http://test/merge\";\n" + " prefix merge;\n" + " container cont {\n" + " choice ch {\n" + " container inner {\n" + " leaf p1 {\n" + " type string;\n" + " }\n" + " }\n" + " case c2 {\n" + " leaf p1 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + const char *trg = + "<cont xmlns=\"http://test/merge\">\n" + " <inner>\n" + " <p1>1</p1>\n" + " </inner>\n" + "</cont>\n"; + const char *src = + "<cont xmlns=\"http://test/merge\">\n" + " <p1>1</p1>\n" + "</cont>\n"; + const char *result = + "<cont xmlns=\"http://test/merge\">\n" + " <p1>1</p1>\n" + "</cont>\n"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + /* merge them */ + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + /* check the result */ + LYD_TREE_CHECK_CHAR(target, result, 0); + + lyd_free_all(source); + lyd_free_all(target); +} + +static void +test_dflt(void **state) +{ + const char *sch = + "module merge-dflt {\n" + " namespace \"urn:merge-dflt\";\n" + " prefix md;\n" + " container top {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " default \"c_dflt\";\n" + " }\n" + " }\n" + "}\n"; + struct lyd_node *target = NULL; + struct lyd_node *source = NULL; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/c", "c_dflt", 0, &target), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/a", "a_val", 0, &source), LY_SUCCESS); + assert_int_equal(lyd_new_path(source, UTEST_LYCTX, "/merge-dflt:top/b", "b_val", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&source, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_merge_siblings(&target, source, LYD_MERGE_DESTRUCT | LYD_MERGE_DEFAULTS), LY_SUCCESS); + source = NULL; + + /* c should be replaced and now be default */ + assert_string_equal(lyd_child(target)->prev->schema->name, "c"); + assert_true(lyd_child(target)->prev->flags & LYD_DEFAULT); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_dflt2(void **state) +{ + const char *sch = + "module merge-dflt {\n" + " namespace \"urn:merge-dflt\";\n" + " prefix md;\n" + " container top {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " default \"c_dflt\";\n" + " }\n" + " }\n" + "}\n"; + struct lyd_node *target; + struct lyd_node *source; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/c", "c_dflt", 0, &target), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&target, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_new_path(NULL, UTEST_LYCTX, "/merge-dflt:top/a", "a_val", 0, &source), LY_SUCCESS); + assert_int_equal(lyd_new_path(source, UTEST_LYCTX, "/merge-dflt:top/b", "b_val", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&source, NULL, LYD_VALIDATE_PRESENT, NULL), LY_SUCCESS); + + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + + /* c should not be replaced, so c remains not default */ + assert_false(lyd_child(target)->flags & LYD_DEFAULT); + + lyd_free_all(target); + lyd_free_all(source); +} + +static void +test_leafrefs(void **state) +{ + const char *sch = "module x {" + " namespace urn:x;" + " prefix x;" + " list l {" + " key n;" + " leaf n { type string; }" + " leaf t { type string; }" + " leaf r { type leafref { path '/l/n'; } }}}"; + const char *trg = "<l xmlns=\"urn:x\"><n>a</n></l>" + "<l xmlns=\"urn:x\"><n>b</n><r>a</r></l>"; + const char *src = "<l xmlns=\"urn:x\"><n>c</n><r>a</r></l>" + "<l xmlns=\"urn:x\"><n>a</n><t>*</t></l>"; + const char *res = "<l xmlns=\"urn:x\"><n>a</n><t>*</t></l>" + "<l xmlns=\"urn:x\"><n>b</n><r>a</r></l>" + "<l xmlns=\"urn:x\"><n>c</n><r>a</r></l>"; + struct lyd_node *source, *target; + + UTEST_ADD_MODULE(sch, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE(src, source); + LYD_TREE_CREATE(trg, target); + + assert_int_equal(lyd_merge_siblings(&target, source, 0), LY_SUCCESS); + + LYD_TREE_CHECK_CHAR(target, res, LYD_PRINT_SHRINK); + + lyd_free_all(source); + lyd_free_all(target); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_batch), + UTEST(test_leaf), + UTEST(test_container), + UTEST(test_list), + UTEST(test_list2), + UTEST(test_dup_inst_list), + UTEST(test_dup_inst_llist), + UTEST(test_case), + UTEST(test_dflt), + UTEST(test_dflt2), + UTEST(test_leafrefs), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c new file mode 100644 index 0000000..7642960 --- /dev/null +++ b/tests/utests/data/test_new.c @@ -0,0 +1,446 @@ +/** + * @file test_new.c + * @author: Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for functions for creating data + * + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "libyang.h" + +/* common module for the tests */ +const char *schema_a = "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;yang-version 1.1;\n" + " list l1 {\n" + " key \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " }\n" + " list l11 {\n" + " key \"a\";\n" + " leaf a {\n" + " type uint32;\n" + " }\n" + " leaf b {\n" + " type uint32;\n" + " }\n" + " }\n" + " leaf foo {\n" + " type uint16;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " container c {\n" + " leaf-list x {\n" + " type string;\n" + " }\n" + " }\n" + " anydata any {\n" + " config false;\n" + " }\n" + " leaf-list ll2 {\n" + " config false;\n" + " type string;\n" + " }\n" + " list l2 {\n" + " config false;\n" + " container c {\n" + " leaf x {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container c2 {\n" + " config false;\n" + " list l3 {\n" + " leaf x {\n" + " type string;\n" + " }\n" + " leaf y {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " rpc oper {\n" + " input {\n" + " leaf param {\n" + " type string;\n" + " }\n" + " }\n" + " output {\n" + " leaf param {\n" + " type int8;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + +static void +test_top_level(void **state) +{ + struct lys_module *mod; + struct lyd_node *node, *rpc; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod); + + /* list */ + assert_int_equal(lyd_new_list(NULL, mod, "l1", 0, &node, "val_a", "val_b"), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[]", 0, &node), LY_EVALID); + CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]\").", "Schema location \"/a:l1\"."); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[key1='a'][key2='b']", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Not found node \"key1\" in path.", "Schema location \"/a:l1\"."); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a='a'][b='b'][c='c']", 0, &node), LY_EVALID); + CHECK_LOG_CTX("Key expected instead of leaf \"c\" in path.", "Schema location \"/a:l1\"."); + + assert_int_equal(lyd_new_list2(NULL, mod, "c", "[a='a'][b='b']", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("List node \"c\" not found.", NULL); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a='a'][b='b']", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a=''][b='']", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a:a='a'][a:b='b']", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a= 'a']\n[b =\t'b']", 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\"."); + + assert_int_equal(lyd_new_term(NULL, mod, "c", "value", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Term node \"c\" not found.", NULL); + + assert_int_equal(lyd_new_term(NULL, mod, "foo", "256", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + /* leaf-list */ + assert_int_equal(lyd_new_term(NULL, mod, "ll", "ahoy", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + /* container */ + assert_int_equal(lyd_new_inner(NULL, mod, "c", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_inner(NULL, mod, "l1", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Inner node (container, notif, RPC, or action) \"l1\" not found.", NULL); + + assert_int_equal(lyd_new_inner(NULL, mod, "l2", 0, &node), LY_ENOTFOUND); + CHECK_LOG_CTX("Inner node (container, notif, RPC, or action) \"l2\" not found.", NULL); + + /* anydata */ + assert_int_equal(lyd_new_any(NULL, mod, "any", "{\"node\":\"val\"}", 0, LYD_ANYDATA_STRING, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + assert_int_equal(lyd_new_any(NULL, mod, "any", "<node>val</node>", 0, LYD_ANYDATA_STRING, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + /* key-less list */ + assert_int_equal(lyd_new_list2(NULL, mod, "l2", "[a='a'][b='b']", 0, &node), LY_EVALID); + CHECK_LOG_CTX("List predicate defined for keyless list \"l2\" in path.", "Schema location \"/a:l2\"."); + + assert_int_equal(lyd_new_list2(NULL, mod, "l2", "", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list2(NULL, mod, "l2", NULL, 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + assert_int_equal(lyd_new_list(NULL, mod, "l2", 0, &node), LY_SUCCESS); + lyd_free_tree(node); + + /* RPC */ + assert_int_equal(lyd_new_inner(NULL, mod, "oper", 0, &rpc), LY_SUCCESS); + assert_int_equal(lyd_new_term(rpc, mod, "param", "22", 0, &node), LY_SUCCESS); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_node_leaf *)node->schema)->type->basetype); + assert_int_equal(lyd_new_term(rpc, mod, "param", "22", 1, &node), LY_SUCCESS); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_node_leaf *)node->schema)->type->basetype); + lyd_free_tree(rpc); +} + +static void +test_opaq(void **state) +{ + struct lyd_node *root, *node; + struct lyd_node_opaq *opq; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, NULL); + + assert_int_equal(lyd_new_opaq(NULL, UTEST_LYCTX, "node1", NULL, NULL, "my-module", &root), LY_SUCCESS); + assert_null(root->schema); + opq = (struct lyd_node_opaq *)root; + assert_string_equal(opq->name.name, "node1"); + assert_string_equal(opq->name.module_name, "my-module"); + assert_string_equal(opq->value, ""); + + assert_int_equal(lyd_new_opaq(root, NULL, "node2", "value", NULL, "my-module2", &node), LY_SUCCESS); + assert_null(node->schema); + opq = (struct lyd_node_opaq *)node; + assert_string_equal(opq->name.name, "node2"); + assert_string_equal(opq->name.module_name, "my-module2"); + assert_string_equal(opq->value, "value"); + assert_ptr_equal(opq->parent, root); + + lyd_free_tree(root); +} + +static void +test_path(void **state) +{ + LY_ERR ret; + struct lyd_node *root, *node, *parent; + struct lys_module *mod; + char *str; + + UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod); + + /* create 2 nodes */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:c/x[.='val']", "vvv", 0, 0, 0, &root, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(root->schema->name, "c"); + assert_non_null(node); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val", lyd_get_value(node)); + + /* append another */ + ret = lyd_new_path2(root, NULL, "/a:c/x", "val2", 0, 0, 0, &parent, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_ptr_equal(parent, node); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val2", lyd_get_value(node)); + + /* and a last one */ + ret = lyd_new_path2(root, NULL, "x", "val3", 0, 0, 0, &parent, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_ptr_equal(parent, node); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val3", lyd_get_value(node)); + + lyd_free_tree(root); + + /* try LYD_NEWOPT_OPAQ */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:l1", NULL, 0, 0, 0, NULL, NULL); + assert_int_equal(ret, LY_EINVAL); + CHECK_LOG_CTX("Predicate missing for list \"l1\" in path \"/a:l1\".", "Schema location \"/a:l1\"."); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:l1", NULL, 0, 0, LYD_NEW_PATH_OPAQ, NULL, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_null(root->schema); + + lyd_free_tree(root); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:foo", NULL, 0, 0, 0, NULL, NULL); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Invalid type uint16 empty value.", "Schema location \"/a:foo\"."); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:foo", NULL, 0, 0, LYD_NEW_PATH_OPAQ, NULL, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_null(root->schema); + + lyd_free_tree(root); + + ret = lyd_new_path(NULL, UTEST_LYCTX, "/a:l11", NULL, LYD_NEW_PATH_OPAQ, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_null(root->schema); + + ret = lyd_new_path(root, NULL, "a", NULL, LYD_NEW_PATH_OPAQ, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(lyd_child(root)); + assert_null(lyd_child(root)->schema); + + ret = lyd_new_path(root, NULL, "b", NULL, LYD_NEW_PATH_OPAQ, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(lyd_child(root)->next); + assert_null(lyd_child(root)->next->schema); + + lyd_free_tree(root); + + /* key-less list */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:c2/l3/x", "val1", 0, 0, 0, &root, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("val1", lyd_get_value(node)); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[1]", NULL, 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_EEXIST); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[2]/x", "val2", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3/x", "val3", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[4]/x", "empty", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[4]/x", "val4", 0, 0, LYD_NEW_PATH_UPDATE, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[5]/x", "val5", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:c2/l3[6]/x", "val6", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<c2 xmlns=\"urn:tests:a\">\n" + " <l3>\n" + " <x>val1</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val2</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val3</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val4</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val5</x>\n" + " </l3>\n" + " <l3>\n" + " <x>val6</x>\n" + " </l3>\n" + "</c2>\n"); + free(str); + lyd_free_siblings(root); + + /* state leaf-list */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:ll2", "val_first", 0, 0, 0, &root, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(node->schema->name, "ll2"); + assert_string_equal("val_first", lyd_get_value(node)); + + ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_EEXIST); + + ret = lyd_new_path2(root, NULL, "/a:ll2[2]", "val2", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + + ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "val", 0, 0, LYD_NEW_PATH_UPDATE, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, UTEST_LYCTX, "/a:ll2", "val3", 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(node); + + ret = lyd_new_path2(root, NULL, "/a:ll2[3][.='val3']", NULL, 0, 0, 0, NULL, &node); + assert_int_equal(ret, LY_EVALID); + + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "<ll2 xmlns=\"urn:tests:a\">val</ll2>\n" + "<ll2 xmlns=\"urn:tests:a\">val2</ll2>\n" + "<ll2 xmlns=\"urn:tests:a\">val3</ll2>\n"); + free(str); + lyd_free_siblings(root); + + /* anydata */ + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", "<elem>val</elem>", 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, + "<any xmlns=\"urn:tests:a\">\n" + " <elem>val</elem>\n" + "</any>\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:any\": {\n" + " \"elem\": \"val\"\n" + " }\n" + "}\n"); + free(str); + lyd_free_siblings(root); +} + +static void +test_path_ext(void **state) +{ + LY_ERR ret; + struct lyd_node *root, *node; + struct lys_module *mod; + const char *mod_str = "module ext {yang-version 1.1; namespace urn:tests:extensions:ext; prefix e;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template {container c {leaf x {type string;} leaf y {type string;} leaf z {type string;}}}}"; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL)); + + UTEST_ADD_MODULE(mod_str, LYS_IN_YANG, NULL, &mod); + + /* create x */ + ret = lyd_new_ext_path(NULL, &mod->compiled->exts[0], "/ext:c/x", "xxx", 0, &root); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + assert_string_equal(root->schema->name, "c"); + assert_non_null(node = lyd_child(root)); + assert_string_equal(node->schema->name, "x"); + assert_string_equal("xxx", lyd_get_value(node)); + + /* append y */ + ret = lyd_new_ext_path(root, &mod->compiled->exts[0], "/ext:c/y", "yyy", 0, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_string_equal(node->schema->name, "y"); + assert_string_equal("yyy", lyd_get_value(node)); + + /* append z */ + ret = lyd_new_path(root, NULL, "ext:z", "zzz", 0, &node); + assert_int_equal(ret, LY_SUCCESS); + assert_string_equal(node->schema->name, "z"); + assert_string_equal("zzz", lyd_get_value(node)); + + lyd_free_tree(root); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_top_level), + UTEST(test_opaq), + UTEST(test_path), + UTEST(test_path_ext), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c new file mode 100644 index 0000000..d341e31 --- /dev/null +++ b/tests/utests/data/test_parser_json.c @@ -0,0 +1,793 @@ +/* + * @file test_parser_json.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from parser_xml.c + * + * Copyright (c) 2019 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "context.h" +#include "in.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tests_config.h" +#include "tree_data_internal.h" +#include "tree_schema.h" + +static int +setup(void **state) +{ + const char *schema = "module a {namespace urn:tests:a;prefix a;yang-version 1.1; import ietf-yang-metadata {prefix md;}" + "md:annotation hint { type int8;}" + "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;}}" + "}" + "leaf foo { type string;}" + "container c {" + " leaf x {type string;}" + " action act { input { leaf al {type string;} } output { leaf al {type uint8;} } }" + " notification n1 { leaf nl {type string;} }" + "}" + "container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}" + "anydata any {config false;}" + "anyxml axml;" + "leaf-list ll1 { type uint8; }" + "leaf foo2 { type string; default \"default-val\"; }" + "leaf foo3 { type uint32; }" + "notification n2;}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_JSON, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define PARSER_CHECK_ERROR(INPUT, PARSE_OPTION, VALIDATE_OPTION, MODEL, RET_VAL, ERR_MESSAGE, ERR_PATH) \ + assert_int_equal(RET_VAL, lyd_parse_data_mem(UTEST_LYCTX, INPUT, LYD_JSON, PARSE_OPTION, VALIDATE_OPTION, &MODEL));\ + CHECK_LOG_CTX(ERR_MESSAGE, ERR_PATH);\ + assert_null(MODEL) + +#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \ + CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_JSON, PRINT_OPTION) + +static void +test_leaf(void **state) +{ + struct lyd_node *tree; + struct lyd_node_term *leaf; + const char *data; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL)); + + data = "{\"a:foo\":\"foo value\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "foo value"); + + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree->next->next; + + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* make foo2 explicit */ + data = "{\"a:foo2\":\"default-val\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_false(leaf->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* parse foo2 but make it implicit */ + data = "{\"a:foo2\":\"default-val\",\"@a:foo2\":{\"ietf-netconf-with-defaults:default\":true}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + + /* print default values */ + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL_TAG, data); + lyd_free_all(tree); + + /* skip leaf */ + data = "{\"a:cp\":{\"x\":\"val\",\"y\":\"valy\",\"z\":5}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:cp\":{\"y\":\"valy\",\"z\":5}}"); + lyd_free_all(tree); + + /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */ + data = "{\"@a:foo\":{\"a:hint\":1,\"a:hint\":2,\"x:xxx\":{\"value\":\"/x:no/x:yes\"}},\"a:foo\":\"xxx\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + CHECK_LYD_META(tree->meta, 1, "hint", 1, 1, INT8, "1", 1); + CHECK_LYD_META(tree->meta->next, 1, "hint", 0, 1, INT8, "2", 2); + assert_null(tree->meta->next->next); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, + "{\"a:foo\":\"xxx\",\"@a:foo\":{\"a:hint\":1,\"a:hint\":2}}"); + lyd_free_all(tree); + + PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Unknown (or not implemented) YANG module \"x\" of metadata \"x:xxx\".", "Data location \"/@a:foo\", line number 1."); + + /* missing referenced metadata node */ + PARSER_CHECK_ERROR("{\"@a:foo\" : { \"a:hint\" : 1 }}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Missing JSON data instance to be coupled with @a:foo metadata.", "Data location \"/@a:foo\", line number 1."); + + /* missing namespace for meatadata*/ + PARSER_CHECK_ERROR("{\"a:foo\" : \"value\", \"@a:foo\" : { \"hint\" : 1 }}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Metadata in JSON must be namespace-qualified, missing prefix for \"hint\".", + "Schema location \"/a:foo\", line number 1."); + + /* invalid JSON type */ + data = "{\"a:l1\" : [{ \"a\" : \"val-a\", \"b\" : \"val-b\", \"c\" : 1, \"cont\" : { \"e\" : \"0\" } }]}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid non-boolean-encoded boolean value \"0\".", + "Data location \"/a:l1[a='val-a'][b='val-b'][c='1']/cont/e\", line number 1."); + + /* 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)); +} + +static void +test_leaflist(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_term *ll; + + data = "{\"a:ll1\":[10,11]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(ll->value, UINT8, "10", 10); + + assert_non_null(tree->next); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next; + CHECK_LYD_VALUE(ll->value, UINT8, "11", 11); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* accept empty */ + data = "{\"a:ll1\":[]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); + + /* simple metadata */ + data = "{\"a:ll1\":[10,11],\"@a:ll1\":[null,{\"a:hint\":2}]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(ll->value, UINT8, "10", 10); + assert_null(ll->meta); + + assert_non_null(tree->next); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next; + CHECK_LYD_VALUE(ll->value, UINT8, "11", 11); + CHECK_LYD_META(ll->meta, 1, "hint", 0, 1, INT8, "2", 2); + assert_null(ll->meta->next); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* multiple meatadata hint and unknown metadata xxx supposed to be skipped since it is from missing schema */ + data = "{\"@a:ll1\" : [{\"a:hint\" : 1, \"x:xxx\" : { \"value\" : \"/x:no/x:yes\" }, " + "\"a:hint\" : 10},null,{\"a:hint\" : 3}], \"a:ll1\" : [1,2,3]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(ll->value, UINT8, "1", 1); + CHECK_LYD_META(ll->meta, 1, "hint", 1, 1, INT8, "1", 1); + CHECK_LYD_META(ll->meta->next, 1, "hint", 0, 1, INT8, "10", 10); + + assert_non_null(tree->next); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next; + CHECK_LYD_VALUE(ll->value, UINT8, "2", 2); + assert_null(ll->meta); + + assert_non_null(tree->next->next); + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "ll1", + 1, LYS_LEAFLIST, 0, 0, NULL, 0); + ll = (struct lyd_node_term *)tree->next->next; + CHECK_LYD_VALUE(ll->value, UINT8, "3", 3); + CHECK_LYD_META(ll->meta, 1, "hint", 0, 1, INT8, "3", 3); + assert_null(ll->meta->next); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, + "{\"a:ll1\":[1,2,3],\"@a:ll1\":[{\"a:hint\":1,\"a:hint\":10},null,{\"a:hint\":3}]}"); + lyd_free_all(tree); + + /* missing referenced metadata node */ + PARSER_CHECK_ERROR("{\"@a:ll1\":[{\"a:hint\":1}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Missing JSON data instance to be coupled with @a:ll1 metadata.", "Data location \"/@a:ll1\", line number 1."); + + PARSER_CHECK_ERROR("{\"a:ll1\":[1],\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2}]}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Missing JSON data instance #2 of a:ll1 to be coupled with metadata.", "Schema location \"/a:ll1\", line number 1."); + + PARSER_CHECK_ERROR("{\"@a:ll1\":[{\"a:hint\":1},{\"a:hint\":2},{\"a:hint\":3}],\"a:ll1\" : [1, 2]}", 0, LYD_VALIDATE_PRESENT, + tree, LY_EVALID, "Missing JSON data instance #3 to be coupled with @a:ll1 metadata.", + "Data location \"/@a:ll1\", line number 1."); +} + +static void +test_anydata(void **state) +{ + const char *data; + struct lyd_node *tree; + + data = "{\"a:any\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:any\":{}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:any\":{\"node\":20}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_R | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_anyxml(void **state) +{ + const char *data; + struct lyd_node *tree; + + data = "{\"a:axml\":\"some-value in string\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":\"\"}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":55}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":false}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":null}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":[null,true,false]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":[null,true,{\"name\":[25,40, false]}]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* same as anydata tests */ + data = "{\"a:axml\":{\"x:element1\":{\"element2\":\"/a:some/a:path\",\"list\":[{},{\"key\":\"a\"}]}}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:axml\":{}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_STATUS_CURR | LYS_CONFIG_W, 1, "axml", 1, LYS_ANYXML, 0, 0, NULL, 0); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_list(void **state) +{ + const char *data; + struct lyd_node *tree, *iter; + struct lyd_node_inner *list; + struct lyd_node_term *leaf; + + /* check hashes */ + data = "{\"a:l1\":[{\"a\":\"one\",\"b\":\"one\",\"c\":1}]}"; + CHECK_PARSE_LYD(data, 0, 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); + list = (struct lyd_node_inner *)tree; + LY_LIST_FOR(list->child, iter) { + assert_int_not_equal(0, iter->hash); + } + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* accept empty */ + data = "{\"a:l1\":[]}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_null(tree); + + /* missing keys */ + PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\"}]}", 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."); + + PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"a\" : \"a\"}]}", 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("{ \"a:l1\": [ {\"b\" : \"b\", \"a\" : \"a\"}]}", 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."); + + /* key duplicate */ + PARSER_CHECK_ERROR("{ \"a:l1\": [ {\"c\" : 1, \"b\" : \"b\", \"a\" : \"a\", \"c\" : 1}]}", 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."); + + /* keys order, in contrast to XML, JSON accepts keys in any order even in strict mode */ + CHECK_PARSE_LYD("{ \"a:l1\": [ {\"d\" : \"d\", \"a\" : \"a\", \"c\" : 1, \"b\" : \"b\"}]}", 0, 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); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0); + 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, "b", 1, LYS_LEAF, 1, 0, NULL, 0); + 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); + 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); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", + 1, LYS_LEAF, 1, 0, NULL, 0); + 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, "b", + 1, LYS_LEAF, 1, 0, NULL, 0); + 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(NULL, NULL); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, + "{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1}]}"); + lyd_free_all(tree); + + data = "{\"a:cp\":{\"@\":{\"a:hint\":1}}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 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); +} + +static void +test_container(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_inner *cont; + + CHECK_PARSE_LYD("{\"a:c\":{}}", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_true(cont->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{}"); + lyd_free_all(tree); + + data = "{\"a:cp\":{}}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_false(cont->flags & LYD_DEFAULT); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* skip container */ + CHECK_PARSE_LYD("{\"a:unknown\":{\"a\":\"val\",\"b\":5}}", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{}"); + lyd_free_all(tree); +} + +static void +test_opaq(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid value, no flags */ + data = "{\"a:foo3\":[null]}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid non-number-encoded uint32 value \"\".", "Schema location \"/a:foo3\", line number 1."); + + /* 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_JSON, "foo3", 0, 0, NULL, 0, ""); + 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, + "List instance is missing its key \"c\".", + "Data location \"/a:l1[a='val_a'][b='val_b']\", line number 1."); + + /* 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_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* invalid key, no flags */ + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid non-number-encoded int16 value \"val_c\".", + "Data location \"/a:l1[a='val_a'][b='val_b']/c\", line number 1."); + + /* 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_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":{\"val\":\"val_c\"}}]}"; + 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_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\"}]}"; + 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_JSON, "l1", 0, 0, NULL, 0, ""); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* 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\"."); + + /* 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."); + + /* opaque data tree format print */ + data = + "{\n" + " \"ietf-netconf-nmda:get-data\": {\n" + " \"data\": {\n" + " \"ietf-keystore:keystore\": {\n" + " \"asymmetric-keys\": {\n" + " \"asymmetric-key\": [\n" + " {\n" + " \"name\": \"genkey\",\n" + " \"algorithm\": \"rsa2048\"\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " \"ietf-netconf-server:netconf-server\": {\n" + " \"listen\": {\n" + " \"idle-timeout\": 3600,\n" + " \"endpoint\": [\n" + " {\n" + " \"name\": \"default-ssh\",\n" + " \"ssh\": {\n" + " \"tcp-server-parameters\": {\n" + " \"local-address\": \"0.0.0.0\",\n" + " \"local-port\": 830\n" + " },\n" + " \"ssh-server-parameters\": {\n" + " \"server-identity\": {\n" + " \"host-key\": [\n" + " {\n" + " \"name\": \"default-key\",\n" + " \"public-key\": {\n" + " \"keystore-reference\": \"genkey\"\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"client-authentication\": {\n" + " \"supported-authentication-methods\": {\n" + " \"publickey\": [null],\n" + " \"passsword\": [null],\n" + " \"other\": [\n" + " \"interactive\",\n" + " \"gssapi\"\n" + " ]\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + const char *dsc = "Edit data in an NMDA datastore.\n" + "\n" + "If an error condition occurs such that an error severity\n" + "<rpc-error> element is generated, the server will stop\n" + "processing the <edit-data> operation and restore the\n" + "specified configuration to its complete state at\n" + "the start of this <edit-data> operation."; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL))); + + 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\"}]}" + "}}"; + 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_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-data", LYS_RPC, + 0, 0, 0, 0, 0, NULL, 0); + + node = tree; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-data", LYS_RPC, + 0, 0, 0, 0, 0, NULL, 0); + node = lyd_child(node)->next; + CHECK_LYSC_NODE(node->schema, "Inline config content.", 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", + 0, LYS_ANYDATA, 1, 0, NULL, 0); + + node = ((struct lyd_node_any *)node)->value.tree; + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + node = lyd_child(node); + /* z has no value */ + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_JSON, "z", 0, 0, NULL, 0, ""); + 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_JSON, "l1", 0, 0, NULL, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_action(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + + data = "{\"a:c\":{\"act\":{\"al\":\"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_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *ntf; + + data = "{\"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_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "{\"a:n2\":{}}"; + 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_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 0, NULL, 0, 0x4, 1, 0, "n2", 0, 0, NULL, 0); + + assert_non_null(tree); + assert_ptr_equal(ntf, tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + + data = "{\"a:c\":{\"act\":{\"al\":25}}}"; + 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_REPLY_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + node = lyd_child(op); + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_STATUS_CURR | LYS_IS_OUTPUT, 1, "al", 0, LYS_LEAF, 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + /* TODO print only rpc-reply node and then output subtree */ + CHECK_LYD_STRING(lyd_child(op), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:al\":25}"); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:c\":{\"act\":{\"al\":25}}}"); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_leaf, setup), + UTEST(test_leaflist, setup), + UTEST(test_anydata, setup), + UTEST(test_anyxml, setup), + UTEST(test_list, setup), + UTEST(test_container, setup), + UTEST(test_opaq, setup), + UTEST(test_rpc, setup), + UTEST(test_action, setup), + UTEST(test_notification, setup), + UTEST(test_reply, 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 new file mode 100644 index 0000000..7defd9c --- /dev/null +++ b/tests/utests/data/test_parser_xml.c @@ -0,0 +1,836 @@ +/** + * @file test_parser_xml.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for functions from parser_xml.c + * + * Copyright (c) 2019 - 2022 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "context.h" +#include "in.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tree_data_internal.h" +#include "tree_schema.h" + +static int +setup(void **state) +{ + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + " 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;}}" + " }" + " leaf foo { type string;}\n" + " container c {\n" + " leaf x {type string;}\n" + " action act { input { leaf al {type string;} } output { leaf al {type uint8;} } }\n" + " 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" + " leaf foo2 { type string; default \"default-val\"; }\n" + " leaf foo3 { type uint32; }\n" + " notification n2;}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define PARSER_CHECK_ERROR(INPUT, PARSE_OPTION, VALIDATE_OPTION, MODEL, RET_VAL, ERR_MESSAGE, ERR_PATH) \ + assert_int_equal(RET_VAL, lyd_parse_data_mem(UTEST_LYCTX, INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, &MODEL));\ + CHECK_LOG_CTX(ERR_MESSAGE, ERR_PATH);\ + assert_null(MODEL) + +#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \ + CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, PRINT_OPTION) + +static void +test_leaf(void **state) +{ + const char *data = "<foo xmlns=\"urn:tests:a\">foo value</foo>"; + struct lyd_node *tree; + struct lyd_node_term *leaf; + + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL)); + + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "foo value"); + + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree->next->next; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + lyd_free_all(tree); + + /* make foo2 explicit */ + data = "<foo2 xmlns=\"urn:tests:a\">default-val</foo2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_false(leaf->flags & LYD_DEFAULT); + lyd_free_all(tree); + + /* parse foo2 but make it implicit, skip metadata xxx from missing schema */ + data = "<foo2 xmlns=\"urn:tests:a\" xmlns:wd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" " + "wd:default=\"true\" xmlns:x=\"urn:x\" x:xxx=\"false\">default-val</foo2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, 1, "foo2", + 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "default-val"); + assert_true(leaf->flags & LYD_DEFAULT); + lyd_free_all(tree); + + /* invalid value */ + data = "<l1 xmlns=\"urn:tests:a\"><a>val-a</a><b>val-b</b><c>1</c><cont><e>0</e></cont></l1>"; + PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid boolean value \"0\".", + "Data location \"/a:l1[a='val-a'][b='val-b'][c='1']/cont/e\", line number 1."); +} + +static void +test_anydata(void **state) +{ + const char *data; + struct lyd_node *tree; + + data = "<any xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <x:element2 x:attr2=\"test\" xmlns:a=\"urn:tests:a\" xmlns:x=\"urn:x\">a:data</x:element2>\n" + " </element1>\n" + " <element1a/>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_SET_CONFIG, 1, "any", + 1, LYS_ANYDATA, 0, 0, NULL, 0); + + const char *data_expected = + "<any xmlns=\"urn:tests:a\">\n" + " <element1>\n" + " <element2 xmlns=\"urn:x\" xmlns:x=\"urn:x\" x:attr2=\"test\" xmlns:a=\"urn:tests:a\">a:data</element2>\n" + " </element1>\n" + " <element1a/>\n" + "</any>\n"; + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected); + lyd_free_all(tree); +} + +static void +test_list(void **state) +{ + const char *data; + struct lyd_node *tree, *iter; + struct lyd_node_inner *list; + struct lyd_node_term *leaf; + + /* check hashes */ + data = "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b><c>1</c></l1>"; + CHECK_PARSE_LYD(data, 0, 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); + list = (struct lyd_node_inner *)tree; + LY_LIST_FOR(list->child, iter) { + assert_int_not_equal(0, iter->hash); + } + lyd_free_all(tree); + + /* 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."); + + 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."); + + /* 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."); + + /* 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); + 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); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0); + 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, "b", 1, LYS_LEAF, 1, 0, NULL, 0); + 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); + 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("Invalid position of the key \"b\" in a list.", NULL); + lyd_free_all(tree); + + data = "<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b><a>a</a></l1>"; + CHECK_PARSE_LYD(data, 0, 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); + list = (struct lyd_node_inner *)tree; + assert_non_null(leaf = (struct lyd_node_term *)list->child); + CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 0); + 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, "b", 1, LYS_LEAF, 1, 0, NULL, 0); + 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); + lyd_free_all(tree); + + PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid position of the key \"b\" in a list.", "Data location \"/a:l1[c='1']/b\", line number 1."); +} + +static void +test_container(void **state) +{ + struct lyd_node *tree; + struct lyd_node_inner *cont; + + CHECK_PARSE_LYD("<c xmlns=\"urn:tests:a\"/>", 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_true(cont->flags & LYD_DEFAULT); + lyd_free_all(tree); + + CHECK_PARSE_LYD("<cp xmlns=\"urn:tests:a\"/>", 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + cont = (struct lyd_node_inner *)tree; + assert_false(cont->flags & LYD_DEFAULT); + lyd_free_all(tree); +} + +static void +test_opaq(void **state) +{ + const char *data; + struct lyd_node *tree; + + /* invalid value, no flags */ + data = "<foo3 xmlns=\"urn:tests:a\"/>"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid type uint32 empty value.", "Schema location \"/a:foo3\", line number 1."); + + /* 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_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_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<l1 xmlns=\"urn:tests:a\"/>\n"); + lyd_free_all(tree); + + /* missing key, no flags */ + data = "<l1 xmlns=\"urn:tests:a\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <d>val_d</d>\n" + "</l1>\n"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "List instance is missing its key \"c\".", + "Data location \"/a:l1[a='val_a'][b='val_b']\", line number 5."); + + /* 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_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* invalid key, no flags */ + data = "<l1 xmlns=\"urn:tests:a\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + "</l1>\n"; + PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid type int16 value \"val_c\".", + "Data location \"/a:l1[a='val_a'][b='val_b']/c\", line number 4."); + + /* 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_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* opaq flag and fail */ + assert_int_equal(LY_EVALID, lyd_parse_data_mem(UTEST_LYCTX, + "<a xmlns=\"ns\">\n" + " <b>x</b>\n" + " <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."); +} + +static void +test_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + const char *dsc = "The <edit-config> operation loads all or part of a specified\n" + "configuration to the specified target configuration."; + const char *ref = "RFC 6241, Section 7.2"; + const char *feats[] = {"writable-running", NULL}; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats))); + + data = "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <l1 xmlns=\"urn:tests:a\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z nc:operation=\"delete\"/>\n" + " </cp>\n" + " </config>\n" + "</edit-config>\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_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-config", LYS_RPC, + 0, 0, 0, 0, 0, ref, 0); + + assert_non_null(tree); + + node = tree; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-config", LYS_RPC, + 0, 0, 0, 0, 0, ref, 0); + node = lyd_child(node)->next; + dsc = "Inline Config content."; + CHECK_LYSC_NODE(node->schema, dsc, 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", 0, LYS_ANYXML, 1, 0, NULL, 0); + + node = ((struct lyd_node_any *)node)->value.tree; + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + + 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, ""); + 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_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"delete\"/>\n" + " </cp>\n" + " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " </config>\n" + "</edit-config>\n"); + + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_action(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + + data = "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\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_RPC_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\n"); + + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *ntf; + + data = "<c xmlns=\"urn:tests:a\">\n" + " <n1>\n" + " <nl>value</nl>\n" + " </n1>\n" + "</c>\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_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* top-level notif without envelope */ + data = "<n2 xmlns=\"urn:tests:a\"/>\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_YANG, &tree, &ntf)); + ly_in_free(in, 0); + + assert_non_null(ntf); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)ntf->schema, 0, NULL, 0, 0x4, 1, 0, "n2", 0, 0, NULL, 0); + + assert_non_null(tree); + assert_ptr_equal(ntf, tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_reply(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + + data = "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>25</al>\n" + " </act>\n" + "</c>\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_REPLY_YANG, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + node = lyd_child(op); + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_STATUS_CURR | LYS_IS_OUTPUT, 1, "al", 0, LYS_LEAF, 1, 0, NULL, 0); + + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 1, LYS_CONTAINER, 0, 0, NULL, 0); + + /* TODO print only rpc-reply node and then output subtree */ + CHECK_LYD_STRING(lyd_child(op), LYD_PRINT_WITHSIBLINGS, "<al xmlns=\"urn:tests:a\">25</al>\n"); + lyd_free_all(tree); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_netconf_rpc(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + const struct lyd_node *node; + const char *dsc = "The <edit-config> operation loads all or part of a specified\n" + "configuration to the specified target configuration."; + const char *ref = "RFC 6241, Section 7.2"; + const char *feats[] = {"writable-running", NULL}; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats))); + + data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "<edit-config>\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <l1 xmlns=\"urn:tests:a\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z nc:operation=\"delete\"/>\n" + " </cp>\n" + " </config>\n" + "</edit-config>\n" + "</rpc>\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_RPC_NETCONF, &tree, &op)); + ly_in_free(in, 0); + + assert_non_null(op); + + node = tree; + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 1, 0, LY_VALUE_XML, "rpc", 0, 0, 0, 0, ""); + + assert_non_null(tree); + + node = op; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "edit-config", LYS_RPC, + 0, 0, 0, 0, 0, ref, 0); + node = lyd_child(node)->next; + dsc = "Inline Config content."; + CHECK_LYSC_NODE(node->schema, dsc, 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", 0, LYS_ANYXML, 1, 0, NULL, 0); + + node = ((struct lyd_node_any *)node)->value.tree; + CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE, 1, "cp", + 1, LYS_CONTAINER, 0, 0, NULL, 0); + + 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, ""); + 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_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\"/>\n"); + CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS, + "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <target>\n" + " <running/>\n" + " </target>\n" + " <config>\n" + " <cp xmlns=\"urn:tests:a\">\n" + " <z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"delete\"/>\n" + " </cp>\n" + " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">\n" + " <a>val_a</a>\n" + " <b>val_b</b>\n" + " <c>val_c</c>\n" + " </l1>\n" + " </config>\n" + "</edit-config>\n"); + + lyd_free_all(tree); + lyd_free_all(op); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_netconf_action(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree, *op; + + data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">" + "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\n" + "</action>\n" + "</rpc>\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_RPC_NETCONF, &tree, &op)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "action", 0, 0, 0, 0, ""); + + assert_non_null(op); + CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "act", LYS_ACTION, + 1, 0, 0, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\">\n" + " <action xmlns=\"urn:ietf:params:xml:ns:yang:1\"/>\n" + "</rpc>\n"); + CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS, + "<act xmlns=\"urn:tests:a\">\n" + " <al>value</al>\n" + "</act>\n"); + + lyd_free_all(tree); + lyd_free_all(op); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_netconf_reply_or_notification(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *action, *tree, *op, *op2; + + /* parse the action */ + data = "<c xmlns=\"urn:tests:a\">\n" + " <act>\n" + " <al>value</al>\n" + " </act>\n" + "</c>\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_RPC_YANG, &action, &op)); + ly_in_free(in, 0); + + /* parse notification first */ + data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + "<eventTime>2010-12-06T08:00:01Z</eventTime>\n" + "<c xmlns=\"urn:tests:a\">\n" + " <n1>\n" + " <nl>value</nl>\n" + " </n1>\n" + "</c>\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); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 1, LY_VALUE_XML, "notification", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "eventTime", 0, 0, 0, 0, + "2010-12-06T08:00:01Z"); + + assert_non_null(op2); + CHECK_LYSC_NOTIF((struct lysc_node_notif *)op2->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + " <eventTime>2010-12-06T08:00:01Z</eventTime>\n" + "</notification>\n"); + CHECK_LYD_STRING(op2, LYD_PRINT_WITHSIBLINGS, + "<n1 xmlns=\"urn:tests:a\">\n" + " <nl>value</nl>\n" + "</n1>\n"); + + 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" + "</rpc-reply>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 0, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, + "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\"/>\n"); + + lyd_free_all(tree); + /* it was connected to the action, do not free */ + + /* parse an ok reply */ + data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n" + " <ok/>\n" + "</rpc-reply>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_VALUE_XML, "ok", 0, 0, 0, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + + lyd_free_all(tree); + + /* parse an error reply */ + data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n" + " <rpc-error>\n" + " <error-type>rpc</error-type>\n" + " <error-tag>missing-attribute</error-tag>\n" + " <error-severity>error</error-severity>\n" + " <error-info>\n" + " <bad-attribute>message-id</bad-attribute>\n" + " <bad-element>rpc</bad-element>\n" + " </error-info>\n" + " </rpc-error>\n" + "</rpc-reply>\n"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_REPLY_NETCONF, &tree, NULL)); + ly_in_free(in, 0); + + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, ""); + CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 1, LY_VALUE_XML, "rpc-error", 0, 0, 0, 0, ""); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + + lyd_free_all(tree); + + lyd_free_all(action); + + /* wrong namespace, element name, whatever... */ + /* TODO */ +} + +static void +test_filter_attributes(void **state) +{ + const char *data; + struct ly_in *in; + struct lyd_node *tree; + const struct lyd_node *node; + const char *dsc; + const char *ref = "RFC 6241, Section 7.7"; + const char *feats[] = {"writable-running", NULL}; + + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats))); + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "notifications", "2008-07-14", NULL))); + + data = "<get xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" + " <filter type=\"xpath\" select=\"/*\"/>\n" + "</get>\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_RPC_YANG, &tree, NULL)); + ly_in_free(in, 0); + assert_non_null(tree); + + node = tree; + dsc = "Retrieve running configuration and device state information."; + CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR, + 1, 0, 0, 1, "get", LYS_RPC, + 1, 0, 0, 0, 0, ref, 0); + node = lyd_child(node); + dsc = "This parameter specifies the portion of the system\nconfiguration and state data to retrieve."; + CHECK_LYSC_NODE(node->schema, dsc, 1, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "filter", 0, LYS_ANYXML, 1, 0, NULL, 0); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<create-subscription xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n" + " <filter type=\"subtree\">\n" + " <inner-node xmlns=\"my:urn\"/>\n" + " </filter>\n" + "</create-subscription>\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_RPC_YANG, &tree, NULL)); + ly_in_free(in, 0); + assert_non_null(tree); + + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_data_skip(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_term *leaf; + + /* add invalid data to a module that is not implemented */ + data = "<foo xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\"><u/></foo>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + + /* add invalid data to a module that is implemented */ + data = "<fooX xmlns=\"urn:tests:a\"><u/><list><value/></list></fooX>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_null(tree); + + /* first invalid, next valid */ + data = "<fooX xmlns=\"urn:tests:a\"><u/></fooX> <foo xmlns=\"urn:tests:a\">foo value</foo>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "foo", 1, LYS_LEAF, 0, 0, NULL, 0); + leaf = (struct lyd_node_term *)tree; + CHECK_LYD_VALUE(leaf->value, STRING, "foo value"); + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_leaf, setup), + UTEST(test_anydata, setup), + UTEST(test_list, setup), + UTEST(test_container, setup), + UTEST(test_opaq, setup), + UTEST(test_rpc, setup), + UTEST(test_action, setup), + UTEST(test_notification, setup), + UTEST(test_reply, setup), + UTEST(test_netconf_rpc, setup), + UTEST(test_netconf_action, setup), + UTEST(test_netconf_reply_or_notification, setup), + UTEST(test_filter_attributes, setup), + UTEST(test_data_skip, 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 new file mode 100644 index 0000000..d533c41 --- /dev/null +++ b/tests/utests/data/test_printer_xml.c @@ -0,0 +1,343 @@ +/* + * @file test_printer_xml.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from printer_yang.c + * + * Copyright (c) 2019-2020 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <string.h> + +#include "context.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tests_config.h" +#include "tree_schema.h" + +static int +setup(void **state) +{ + const char *schema_defs = "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;" + "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;} identity fast-ethernet {base ethernet;}}"; + const char *schema_types = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}" + "feature f; identity gigabit-ethernet { base defs:ethernet;}" + "container cont {leaf leaftarget {type empty;}" + " list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}" + " action test {input {leaf a {type string;}} output {leaf b {type string;}}}}" + " leaf-list leaflisttarget {type uint8; max-elements 5;}}" + "list list {key id; leaf id {type string;} leaf value {type string;} leaf-list targets {type string;}}" + "list list2 {key \"id value\"; leaf id {type string;} leaf value {type string;}}" + "list list_inst {key id; leaf id {type instance-identifier {require-instance true;}} leaf value {type string;}}" + "list list_ident {key id; leaf id {type identityref {base defs:interface-type;}} leaf value {type string;}}" + "leaf-list leaflisttarget {type string;}" + "leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}" + "leaf binary-norestr {type binary;}" + "leaf int8 {type int8 {range 10..20;}}" + "leaf uint8 {type uint8 {range 150..200;}}" + "leaf int16 {type int16 {range -20..-10;}}" + "leaf uint16 {type uint16 {range 150..200;}}" + "leaf int32 {type int32;}" + "leaf uint32 {type uint32;}" + "leaf int64 {type int64;}" + "leaf uint64 {type uint64;}" + "leaf bits {type bits {bit zero; bit one {if-feature f;} bit two;}}" + "leaf enums {type enumeration {enum white; enum yellow {if-feature f;}}}" + "leaf dec64 {type decimal64 {fraction-digits 1; range 1.5..10;}}" + "leaf dec64-norestr {type decimal64 {fraction-digits 18;}}" + "leaf str {type string {length 8..10; pattern '[a-z ]*';}}" + "leaf str-norestr {type string;}" + "leaf bool {type boolean;}" + "leaf empty {type empty;}" + "leaf ident {type identityref {base defs:interface-type;}}" + "leaf inst {type instance-identifier {require-instance true;}}" + "leaf inst-noreq {type instance-identifier {require-instance false;}}" + "leaf lref {type leafref {path /leaflisttarget; require-instance true;}}" + "leaf lref2 {type leafref {path \"../list[id = current()/../str-norestr]/targets\"; require-instance true;}}" + "leaf un1 {type union {" + " type leafref {path /int8; require-instance true;}" + " type union { type identityref {base defs:interface-type;} type instance-identifier {require-instance true;} }" + " type string {length 1..20;}}}" + "anydata any;" + "rpc sum {input {leaf x {type uint8;} leaf y {type uint8;}} output {leaf result {type uint16;}}}}"; + const char *schema_defaults = + "module defaults {\n" + " namespace \"urn:defaults\";\n" + " prefix d;\n" + " leaf a {\n" + " type union {\n" + " type instance-identifier;\n" + " type string;\n" + " }\n" + " default \"/d:b\";\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + "}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema_defs, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema_types, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema_defaults, LYS_IN_YANG, NULL, NULL); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define CHECK_LYD_STRING(IN_MODEL, PRINT_OPTION, TEXT) \ + CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, PRINT_OPTION) + +static void +test_anydata(void **state) +{ + struct lyd_node *tree; + const char *data; + + data = "<any xmlns=\"urn:tests:types\"><somexml xmlns:x=\"url:x\" xmlns=\"example.com\"><x:x/></somexml></any>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + /* canonized */ + data = "<any xmlns=\"urn:tests:types\"><somexml xmlns=\"example.com\"><x xmlns=\"url:x\"/></somexml></any>"; + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\"/>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <cont>\n" + " <defs:elem1 xmlns:defs=\"urn:tests:defs\">\n" + " <elem2 xmlns:defaults=\"urn:defaults\" defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\">\n" + " </elem2>\n" + " </defs:elem1>\n" + " </cont>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_non_null(tree); + tree = tree->next; + /* cont should be normally parsed */ + CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "any", 0, LYS_ANYDATA, 0, 0, NULL, 0); + CHECK_LYD_NODE_ANY((struct lyd_node_any *)tree, 0, 0, 0, LYD_ANYDATA_DATATREE); + struct lyd_node *tree_tmp = ((struct lyd_node_any *)tree)->value.tree; + + CHECK_LYSC_NODE(tree_tmp->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "cont", 1, LYS_CONTAINER, 0, 0, NULL, 0); + /* but its children not */ + assert_null(((struct lyd_node_inner *)tree_tmp)->child->schema); + /* canonized */ + data = + "<any xmlns=\"urn:tests:types\">\n" + " <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" + " </elem1>\n" + " </cont>\n" + "</any>\n"; + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <ahoj attr=\"<test\">\n" + " ahoj jak se vede < how are you" + " </ahoj>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <leaflisttarget> ahoj </leaflisttarget>\n" + " <leaflisttarget> nazdar </leaflisttarget>\n" + " <leaflisttarget> ÄŒau </leaflisttarget>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <cont2/>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + data = "<any xmlns=\"urn:tests:types\">\n" + " <cont>\n" + " < how are you" + " </cont>\n" + "</any>\n"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); +} + +static void +test_defaults(void **state) +{ + struct lyd_node *tree; + const char *data; + const char *data_trim; + const char *data_all; + const char *data_all_tag; + const char *data_impl_tag; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL)); + + /* standard default value */ + data = "<c xmlns=\"urn:defaults\">aa</c>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + data = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><c xmlns=\"urn:defaults\">aa</c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + data = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"" + " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>" + "<c xmlns=\"urn:defaults\">aa</c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + + data = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"" + " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>" + "<c xmlns=\"urn:defaults\">aa</c>"; + CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* string value equal to the default but default is an unresolved instance-identifier, so they are not considered equal */ + data = "<a xmlns=\"urn:defaults\">/d:b</a><c xmlns=\"urn:defaults\">aa</c>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data); + lyd_free_all(tree); + + /* instance-identifier value equal to the default, should be considered equal */ + data = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + data_trim = "<b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + data_all = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + data_all_tag = "<a xmlns=\"urn:defaults\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\"" + " ncwd:default=\"true\" xmlns:d=\"urn:defaults\">/d:b</a>" + "<b xmlns=\"urn:defaults\">val</b>" + "<c xmlns=\"urn:defaults\">aa</c>"; + data_impl_tag = "<a xmlns=\"urn:defaults\" xmlns:d=\"urn:defaults\">/d:b</a><b xmlns=\"urn:defaults\">val</b><c xmlns=\"urn:defaults\">aa</c>"; + + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_TRIM | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_trim); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_all); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_ALL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_all_tag); + CHECK_LYD_STRING(tree, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data_impl_tag); + lyd_free_all(tree); +} + +#if 0 + +static void +test_rpc(void **state) +{ + struct state_s *s = (struct state_s *)(*state); + struct lyd_node *tree1; + struct lyd_node *tree2; + const struct lyd_node **trees; + const char *request; + const char *reply, *result; + char *printed; + ssize_t len; + struct ly_out *out; + + s->func = test_rpc; + assert_non_null(out = ly_out_new_memory(&printed, 0)); + + request = "<sum xmlns=\"urn:tests:types\"><x>10</x><y>20</y></sum>"; + reply = "<result xmlns=\"urn:tests:types\">30</result>"; + result = "<sum xmlns=\"urn:tests:types\"><result>30</result></sum>"; + assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL)); + assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, request); + ly_out_reset(out); + assert_non_null(trees = lyd_trees_new(1, tree1)); + assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees)); + assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, result); + ly_out_reset(out); + lyd_trees_free(trees, 0); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* no arguments */ + request = "<sum xmlns=\"urn:tests:types\"/>"; + reply = ""; + result = "<sum xmlns=\"urn:tests:types\"/>"; + assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL)); + assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, request); + ly_out_reset(out); + assert_non_null(trees = lyd_trees_new(1, tree1)); + assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees)); + assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, result); + ly_out_reset(out); + lyd_trees_free(trees, 0); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* action + * "container cont {leaf leaftarget {type empty;}" + "list listtarget {key id; max-elements 5;leaf id {type uint8;} leaf value {type string;}" + "action test {input {leaf a {type string;}} output {leaf b {type string;}}}}" + "leaf-list leaflisttarget {type uint8; max-elements 5;}}" + */ + request = "<cont xmlns=\"urn:tests:types\"><listtarget><id>10</id><test><a>test</a></test></listtarget></cont>"; + reply = "<b xmlns=\"urn:tests:types\">test-reply</b>"; + result = "<cont xmlns=\"urn:tests:types\"><listtarget><id>10</id><test><b>test-reply</b></test></listtarget></cont>"; + assert_non_null(tree1 = lyd_parse_mem(s->ctx, request, LYD_XML, LYD_OPT_RPC, NULL)); + assert_true((len = lyd_print_tree(out, tree1, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, request); + ly_out_reset(out); + assert_non_null(trees = lyd_trees_new(1, tree1)); + assert_non_null(tree2 = lyd_parse_mem(s->ctx, reply, LYD_XML, LYD_OPT_RPCREPLY, trees)); + assert_true((len = lyd_print_tree(out, tree2, LYD_XML, LYD_PRINT_SHRINK)) >= 0); + assert_int_equal(len, strlen(printed)); + assert_string_equal(printed, result); + ly_out_reset(out); + lyd_trees_free(trees, 0); + lyd_free_all(tree1); + lyd_free_all(tree2); + + ly_out_free(out, NULL, 1); + s->func = NULL; +} + +#endif + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_anydata, setup), + UTEST(test_defaults, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c new file mode 100644 index 0000000..27dba42 --- /dev/null +++ b/tests/utests/data/test_tree_data.c @@ -0,0 +1,597 @@ +/** + * @file test_tree_data.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from tress_data.c + * + * Copyright (c) 2018-2019 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "common.h" +#include "libyang.h" +#include "path.h" +#include "xpath.h" + +static int +setup(void **state) +{ + const char *schema1 = "module a {namespace urn:tests:a;prefix a;yang-version 1.1;" + "revision 2014-05-08;" + "leaf bar {type string;}" + "list l1 { key \"a b\"; leaf a {type string;} leaf b {type string;} leaf c {type string;}}" + "leaf foo { type string;}" + "leaf-list ll { type string;}" + "container c {leaf-list x {type string;}}" + "anydata any {config false;}" + "list l2 {config false;" + " container c{leaf x {type string;} leaf-list d {type string;}}" + "}}"; + + const char *schema2 = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}}" + "anydata any {config false;}" + "}"; + + const char *schema3 = "module c {yang-version 1.1; namespace \"http://example.com/main\";prefix m;" + "import \"ietf-inet-types\" {prefix inet;}" + "typedef optional-ip-address {type union {" + " type inet:ip-address;" + " type string;" + "}}" + "container cont {" + " list nexthop {min-elements 1; key \"gateway\";" + " leaf gateway {type optional-ip-address;}" + " }" + " leaf-list pref {type inet:ipv6-prefix;}" + "}}"; + + UTEST_SETUP; + + UTEST_ADD_MODULE(schema1, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(schema3, LYS_IN_YANG, NULL, NULL); + + return 0; +} + +#define CHECK_PARSE_LYD(INPUT, PARSE_OPTION, VALIDATE_OPTION, TREE) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, PARSE_OPTION, VALIDATE_OPTION, LY_SUCCESS, TREE) + +#define CHECK_PARSE_LYD_PARAM_CTX(CTX, INPUT, PARSE_OPTION, OUT_NODE) \ + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(CTX, INPUT, LYD_XML, PARSE_OPTION, LYD_VALIDATE_PRESENT, &OUT_NODE)); \ + assert_non_null(OUT_NODE); + +#define RECREATE_CTX_WITH_MODULE(CTX, MODULE) \ + ly_ctx_destroy(CTX); \ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &CTX)); \ + assert_int_equal(LY_SUCCESS, ly_in_new_memory(MODULE, &_UC->in)); \ + assert_int_equal(LY_SUCCESS, lys_parse(CTX, _UC->in, LYS_IN_YANG, NULL, NULL)); \ + ly_in_free(_UC->in, 0); + +static void +test_compare(void **state) +{ + struct lyd_node *tree1, *tree2; + const char *data1; + const char *data2; + + assert_int_equal(LY_SUCCESS, lyd_compare_single(NULL, NULL, 0)); + + data1 = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>"; + data2 = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>y</c></l1>"; + 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, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + assert_int_equal(LY_ENOT, lyd_compare_single(((struct lyd_node_inner *)tree1)->child, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + 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_SUCCESS, lyd_compare_single(tree1->next->next, tree2->next, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<ll xmlns=\"urn:tests:a\">a</ll><ll xmlns=\"urn:tests:a\">b</ll>"; + data2 = "<ll xmlns=\"urn:tests:a\">b</ll>"; + 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, tree2, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(NULL, tree2, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, NULL, 0)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<c xmlns=\"urn:tests:a\"><x>x</x></c>"; + data2 = "<c xmlns=\"urn:tests:a\"><x>y</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, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<c xmlns=\"urn:tests:a\"><x>x</x></c>"; + data2 = "<c xmlns=\"urn:tests:a\"><x>x</x><x>y</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, 0)); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data1 = "<any xmlns=\"urn:tests:a\"><x>x</x></any>"; + data2 = "<any xmlns=\"urn:tests:a\"><x>x</x><x>y</x></any>"; + 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)); + lyd_free_all(tree1); + data1 = "<any xmlns=\"urn:tests:a\"><x>x</x><x>y</x></any>"; + CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); +} + +static void +test_compare_diff_ctx(void **state) +{ + struct lyd_node *tree1, *tree2; + const char *data1, *data2; + struct ly_ctx *ctx2 = NULL; + const char *module; + + /* create second context with the same schema */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + 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_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has a different name */ + module = "module c {namespace urn:tests:c;prefix c;yang-version 1.1;" + "revision 2014-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + data2 = "<l2 xmlns=\"urn:tests:c\"><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)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has a different revision */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2015-05-08;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + 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)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has no revision */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "list l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + 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)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with schema that has a different parent nodetype */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "container l2 {config false;" + " container c{leaf x {type string;}}" + "}}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>"; + 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(lyd_child(lyd_child(tree1)), lyd_child(lyd_child(tree2)), 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with the same opaq data nodes */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "anydata any {config false;}" + "}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>"; + data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>bb:x</x></any>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with the different opaq data node value */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "anydata any {config false;}" + "}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>"; + data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>bb:y</x></any>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* recreate second context with the wrong prefix in opaq data node value */ + module = "module b {namespace urn:tests:b;prefix b;yang-version 1.1;" + "revision 2014-05-08;" + "anydata any {config false;}" + "}"; + RECREATE_CTX_WITH_MODULE(ctx2, module); + data1 = "<any xmlns=\"urn:tests:b\" xmlns:aa=\"urn:tests:b\"><x>aa:x</x></any>"; + data2 = "<any xmlns=\"urn:tests:b\" xmlns:bb=\"urn:tests:b\"><x>cc:x</x></any>"; + CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, LYD_PARSE_ONLY, tree1); + CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, LYD_PARSE_ONLY, tree2); + assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* clean up */ + ly_ctx_destroy(ctx2); + _UC->in = NULL; +} + +static void +test_dup(void **state) +{ + struct lyd_node *tree1, *tree2; + const char *result; + const char *data; + + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, LYD_DUP_RECURSIVE, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>x</c></l1>"; + result = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, 0, &tree2)); + lyd_free_all(tree1); + CHECK_PARSE_LYD(result, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + result = "<l2 xmlns=\"urn:tests:a\"><c><x>a</x></c></l2>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_siblings(tree1, NULL, LYD_DUP_RECURSIVE, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2->next, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree2); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, LYD_DUP_RECURSIVE, &tree2)); + lyd_free_all(tree1); + CHECK_PARSE_LYD(result, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree2); + + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, 0, &tree2)); + lyd_free_all(tree1); + result = "<l2 xmlns=\"urn:tests:a\"/>"; + CHECK_PARSE_LYD_PARAM(result, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree1); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<any xmlns=\"urn:tests:a\"><c><a>a</a></c></any>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1, NULL, 0, &tree2)); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + 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)); + 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); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, (struct lyd_node *)tree2->parent->parent, + LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>c</c></l1>"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1); + assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)tree1)->child->prev, NULL, + LYD_DUP_WITH_PARENTS, &tree2)); + flag = LYS_CONFIG_W | LYS_SET_ENUM; + CHECK_LYSC_NODE(tree2->schema, NULL, 0, flag, 1, "c", 0, LYS_LEAF, 1, 0, NULL, 0); + assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, (struct lyd_node *)tree2->parent, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + 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_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION)); + lyd_free_all(tree1); + lyd_free_all(tree2); + + /* invalid */ + data = "<l1 xmlns=\"urn:tests:a\"><a>a</a><b>b</b><c>c</c></l1><l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>"; + 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)); + lyd_free_all(tree1); +} + +static void +test_target(void **state) +{ + const struct lyd_node_term *term; + struct lyd_node *tree; + struct lyxp_expr *exp; + struct ly_path *path; + const char *path_str = "/a:l2[2]/c/d[3]"; + const char *data = + "<l2 xmlns=\"urn:tests:a\"><c>" + " <d>a</d>" + " </c></l2>" + "<l2 xmlns=\"urn:tests:a\"><c>" + " <d>a</d>" + " <d>b</d>" + " <d>b</d>" + " <d>c</d>" + "</c></l2>" + "<l2 xmlns=\"urn:tests:a\"><c>" + "</c></l2>"; + + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + assert_int_equal(LY_SUCCESS, ly_path_parse(UTEST_LYCTX, NULL, path_str, strlen(path_str), 0, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &exp)); + assert_int_equal(LY_SUCCESS, ly_path_compile(UTEST_LYCTX, NULL, NULL, NULL, exp, LY_PATH_OPER_INPUT, + LY_PATH_TARGET_SINGLE, 1, LY_VALUE_JSON, NULL, &path)); + term = lyd_target(path, tree); + + const int unsigned flag = LYS_CONFIG_R | LYS_SET_ENUM | LYS_ORDBY_USER; + + CHECK_LYSC_NODE(term->schema, NULL, 0, flag, 1, "d", 0, LYS_LEAFLIST, 1, 0, NULL, 0); + assert_string_equal(lyd_get_value(&term->node), "b"); + assert_string_equal(lyd_get_value(term->prev), "b"); + + lyd_free_all(tree); + ly_path_free(UTEST_LYCTX, path); + lyxp_expr_free(UTEST_LYCTX, exp); +} + +static void +test_list_pos(void **state) +{ + const char *data; + struct lyd_node *tree; + + data = "<bar xmlns=\"urn:tests:a\">test</bar>" + "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b></l1>" + "<l1 xmlns=\"urn:tests:a\"><a>two</a><b>two</b></l1>" + "<foo xmlns=\"urn:tests:a\">test</foo>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_int_equal(0, lyd_list_pos(tree)); + assert_int_equal(1, lyd_list_pos(tree->next)); + assert_int_equal(2, lyd_list_pos(tree->next->next)); + assert_int_equal(0, lyd_list_pos(tree->next->next->next)); + lyd_free_all(tree); + + data = "<ll xmlns=\"urn:tests:a\">one</ll>" + "<ll xmlns=\"urn:tests:a\">two</ll>" + "<ll xmlns=\"urn:tests:a\">three</ll>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_int_equal(1, lyd_list_pos(tree)); + assert_int_equal(2, lyd_list_pos(tree->next)); + assert_int_equal(3, lyd_list_pos(tree->next->next)); + lyd_free_all(tree); + + data = "<ll xmlns=\"urn:tests:a\">one</ll>" + "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b></l1>" + "<ll xmlns=\"urn:tests:a\">two</ll>" + "<l1 xmlns=\"urn:tests:a\"><a>two</a><b>two</b></l1>" + "<ll xmlns=\"urn:tests:a\">three</ll>" + "<l1 xmlns=\"urn:tests:a\"><a>three</a><b>three</b></l1>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_string_equal("l1", tree->schema->name); + assert_int_equal(1, lyd_list_pos(tree)); + assert_int_equal(2, lyd_list_pos(tree->next)); + assert_int_equal(3, lyd_list_pos(tree->next->next)); + assert_string_equal("ll", tree->next->next->next->schema->name); + assert_int_equal(1, lyd_list_pos(tree->next->next->next)); + assert_int_equal(2, lyd_list_pos(tree->next->next->next->next)); + assert_int_equal(3, lyd_list_pos(tree->next->next->next->next->next)); + lyd_free_all(tree); +} + +static void +test_first_sibling(void **state) +{ + const char *data; + struct lyd_node *tree; + struct lyd_node_inner *parent; + + data = "<bar xmlns=\"urn:tests:a\">test</bar>" + "<l1 xmlns=\"urn:tests:a\"><a>one</a><b>one</b><c>one</c></l1>" + "<foo xmlns=\"urn:tests:a\">test</foo>"; + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + assert_ptr_equal(tree, lyd_first_sibling(tree->next)); + assert_ptr_equal(tree, lyd_first_sibling(tree)); + assert_ptr_equal(tree, lyd_first_sibling(tree->prev)); + parent = (struct lyd_node_inner *)tree->next; + assert_int_equal(LYS_LIST, parent->schema->nodetype); + assert_ptr_equal(parent->child, lyd_first_sibling(parent->child->next)); + assert_ptr_equal(parent->child, lyd_first_sibling(parent->child)); + assert_ptr_equal(parent->child, lyd_first_sibling(parent->child->prev)); + lyd_free_all(tree); +} + +static void +test_find_path(void **state) +{ + struct lyd_node *root; + const struct lys_module *mod; + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "c"); + assert_non_null(mod); + + assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "cont", 0, &root)); + assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='10.0.0.1']", NULL, LYD_NEW_PATH_UPDATE, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='2100::1']", NULL, LYD_NEW_PATH_UPDATE, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/pref[.='fc00::/64']", NULL, 0, NULL)); + + 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)); + lyd_free_all(root); +} + +static void +test_data_hash(void **state) +{ + struct lyd_node *tree; + const char *schema, *data; + + schema = + "module test-data-hash {" + " yang-version 1.1;" + " namespace \"urn:tests:tdh\";" + " prefix t;" + " container c {" + " leaf-list ll {" + " type string;" + " }" + " }" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + /* The number of <ll/> must be greater or equal to LYD_HT_MIN_ITEMS + * for the correct test run. It should guarantee the creation of a hash table. + */ + assert_true(LYD_HT_MIN_ITEMS <= 4); + data = + "<c xmlns='urn:tests:tdh'>" + " <ll/>" + " <ll/>" + " <ll/>" + " <ll/>" + "</c>"; + + /* 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); + lyd_free_all(tree); +} + +static void +test_lyxp_vars(void **UNUSED(state)) +{ + struct lyxp_var *vars; + + /* Test free. */ + vars = NULL; + lyxp_vars_free(vars); + + /* Bad arguments for lyxp_vars_add(). */ + assert_int_equal(LY_EINVAL, lyxp_vars_set(NULL, "var1", "val1")); + assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, NULL, "val1")); + assert_int_equal(LY_EINVAL, lyxp_vars_set(&vars, "var1", NULL)); + lyxp_vars_free(vars); + vars = NULL; + + /* Add one item. */ + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1")); + assert_int_equal(LY_ARRAY_COUNT(vars), 1); + assert_string_equal(vars[0].name, "var1"); + assert_string_equal(vars[0].value, "val1"); + lyxp_vars_free(vars); + vars = NULL; + + /* Add three items. */ + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var3", "val3")); + assert_int_equal(LY_ARRAY_COUNT(vars), 3); + assert_string_equal(vars[0].name, "var1"); + assert_string_equal(vars[0].value, "val1"); + assert_string_equal(vars[1].name, "var2"); + assert_string_equal(vars[1].value, "val2"); + assert_string_equal(vars[2].name, "var3"); + assert_string_equal(vars[2].value, "val3"); + lyxp_vars_free(vars); + vars = NULL; + + /* Change value of a variable. */ + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "val1")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "val2")); + assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "new_value")); + assert_string_equal(vars[0].name, "var1"); + assert_string_equal(vars[0].value, "new_value"); + assert_string_equal(vars[1].name, "var2"); + assert_string_equal(vars[1].value, "val2"); + lyxp_vars_free(vars); + vars = NULL; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_compare, setup), + UTEST(test_compare_diff_ctx, setup), + UTEST(test_dup, setup), + UTEST(test_target, setup), + UTEST(test_list_pos, setup), + UTEST(test_first_sibling, setup), + UTEST(test_find_path, setup), + UTEST(test_data_hash, setup), + UTEST(test_lyxp_vars), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c new file mode 100644 index 0000000..415e16a --- /dev/null +++ b/tests/utests/data/test_validation.c @@ -0,0 +1,1460 @@ +/** + * @file test_validation.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for functions from validation.c + * + * Copyright (c) 2020 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include <stdio.h> +#include <string.h> + +#include "context.h" +#include "in.h" +#include "out.h" +#include "parser_data.h" +#include "printer_data.h" +#include "tests_config.h" +#include "tree_data_internal.h" +#include "tree_schema.h" + +#define LYD_TREE_CREATE(INPUT, MODEL) \ + CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, MODEL) + +static void +test_when(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " leaf a {\n" + " when \"../../c = 'val_c'\";\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " leaf c {\n" + " when \"/cont/b = 'val_b'\";\n" + " type string;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<c xmlns=\"urn:tests:a\">hey</c>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("When condition \"/cont/b = 'val_b'\" not satisfied.", "Data location \"/a:c\"."); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><b>val_b</b></cont><c xmlns=\"urn:tests:a\">hey</c>", tree); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 0, LYS_LEAF, 0, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, tree->next->flags); + lyd_free_all(tree); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val</a><b>val_b</b></cont><c xmlns=\"urn:tests:a\">val_c</c>", tree); + CHECK_LYSC_NODE(lyd_child(tree)->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "a", 1, LYS_LEAF, 1, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, lyd_child(tree)->flags); + CHECK_LYSC_NODE(tree->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "c", 0, LYS_LEAF, 0, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, tree->next->flags); + lyd_free_all(tree); +} + +static void +test_mandatory_when(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " when \"../a = 'val_a'\";\n" + " mandatory true;\n" + " type string;\n" + " }\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " leaf d {\n" + " when \"../c = 'val_c'\";\n" + " mandatory true;\n" + " type string;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:a\">hey</d>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("When condition \"../c = 'val_c'\" not satisfied.", "Data location \"/a:d\"."); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:a\"><b>hey</b></cont>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("When condition \"../a = 'val_a'\" not satisfied.", "Data location \"/a:cont/b\"."); + + LYD_TREE_CREATE("<c xmlns=\"urn:tests:a\">val_c</c><d xmlns=\"urn:tests:a\">hey</d>", tree); + CHECK_LYSC_NODE(tree->next->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "d", 0, LYS_LEAF, 0, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, tree->next->next->flags); + lyd_free_all(tree); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val_a</a><b>hey</b></cont>", tree); + CHECK_LYSC_NODE(lyd_child(tree)->next->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "b", 0, LYS_LEAF, tree->schema, 0, NULL, 1); + assert_int_equal(LYD_WHEN_TRUE, lyd_child(tree)->next->flags); + lyd_free_all(tree); +} + +static void +test_type_incomplete_when(void **state) +{ + struct lys_module *mod; + struct lyd_node *tree; + const char *schema = + "module a {\n" + " namespace urn:tests:a;\n" + " prefix a;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " when \"../c = 'val_c'\";\n" + " leaf a {\n" + " type leafref {\n" + " path \"/a:c\";\n" + " }\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:a\"><a>val_c</a><b>val</b></cont><c xmlns=\"urn:tests:a\">val_c</c>", tree); + + /* make the when false */ + assert_int_equal(LY_SUCCESS, lyd_change_term(tree->next, "wrong-val")); + + /* autodelete when with a leafref */ + assert_int_equal(LY_SUCCESS, lyd_validate_module(&tree, mod, 0, NULL)); + assert_string_equal(LYD_NAME(tree), "c"); + assert_null(tree->next); + + lyd_free_all(tree); +} + +static void +test_mandatory(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module b {\n" + " namespace urn:tests:b;\n" + " prefix b;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " mandatory true;\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " leaf c {\n" + " mandatory true;\n" + " type string;\n" + " }\n" + " leaf d {\n" + " type empty;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:b\"/>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Mandatory choice \"choic\" data do not exist.", "Schema location \"/b:choic\".", "missing-choice"); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:b\">string</l><d xmlns=\"urn:tests:b\"/>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Mandatory node \"c\" instance does not exist.", "Schema location \"/b:c\"."); + + CHECK_PARSE_LYD_PARAM("<a xmlns=\"urn:tests:b\">string</a>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Mandatory node \"c\" instance does not exist.", "Schema location \"/b:c\"."); + + LYD_TREE_CREATE("<a xmlns=\"urn:tests:b\">string</a><c xmlns=\"urn:tests:b\">string2</c>", tree); + lyd_free_siblings(tree); +} + +static void +test_minmax(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module c {\n" + " namespace urn:tests:c;\n" + " prefix c;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf-list l {\n" + " min-elements 3;\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " list lt {\n" + " max-elements 4;\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " }\n" + " leaf d {\n" + " type empty;\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">mate</l>" + "<d xmlns=\"urn:tests:c\"/>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "Schema location \"/c:choic/b/l\".", "too-few-elements"); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">val1</l>" + "<l xmlns=\"urn:tests:c\">val2</l>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "Schema location \"/c:choic/b/l\".", "too-few-elements"); + + LYD_TREE_CREATE("<l xmlns=\"urn:tests:c\">val1</l>" + "<l xmlns=\"urn:tests:c\">val2</l>" + "<l xmlns=\"urn:tests:c\">val3</l>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:c\">val1</l>" + "<l xmlns=\"urn:tests:c\">val2</l>" + "<l xmlns=\"urn:tests:c\">val3</l>" + "<lt xmlns=\"urn:tests:c\"><k>val1</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val2</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val3</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val4</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val5</k></lt>" + "<lt xmlns=\"urn:tests:c\"><k>val6</k></lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Too many \"lt\" instances.", "Data location \"/c:lt[k='val5']\".", + "too-many-elements"); +} + +const char *schema_d = + "module d {\n" + " namespace urn:tests:d;\n" + " prefix d;\n" + " yang-version 1.1;\n" + "\n" + " list lt {\n" + " key \"k\";\n" + " unique \"l1\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " leaf l1 {\n" + " type string;\n" + " }\n" + " }\n" + " list lt2 {\n" + " key \"k\";\n" + " unique \"cont/l2 l4\";\n" + " unique \"l5 l6\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " container cont {\n" + " leaf l2 {\n" + " type string;\n" + " }\n" + " }\n" + " leaf l4 {\n" + " type string;\n" + " }\n" + " leaf l5 {\n" + " type string;\n" + " }\n" + " leaf l6 {\n" + " type string;\n" + " }\n" + " list lt3 {\n" + " key \"kk\";\n" + " unique \"l3\";\n" + " leaf kk {\n" + " type string;\n" + " }\n" + " leaf l3 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}"; + +static void +test_unique(void **state) +{ + struct lyd_node *tree; + + UTEST_ADD_MODULE(schema_d, LYS_IN_YANG, NULL, NULL); + + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>same</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + "</lt>", tree); + lyd_free_all(tree); + + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>same</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>not-same</l1>\n" + "</lt>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>same</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>same</l1>\n" + "</lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val1']\" and \"/d:lt[k='val2']\".", + "Data location \"/d:lt[k='val2']\".", "data-not-unique"); + + /* now try with more instances */ + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>1</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <l1>3</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <l1>4</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <l1>5</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val6</k>\n" + " <l1>6</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val7</k>\n" + " <l1>7</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val8</k>\n" + " <l1>8</l1>\n" + "</lt>", tree); + lyd_free_all(tree); + + LYD_TREE_CREATE("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>1</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <l1>3</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <l1>5</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val6</k>\n" + " <l1>6</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val7</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val8</k>\n" + "</lt>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <l1>1</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <l1>4</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val6</k>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val7</k>\n" + " <l1>2</l1>\n" + "</lt>\n" + "<lt xmlns=\"urn:tests:d\">\n" + " <k>val8</k>\n" + " <l1>8</l1>\n" + "</lt>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l1\" not satisfied in \"/d:lt[k='val7']\" and \"/d:lt[k='val2']\".", + "Data location \"/d:lt[k='val2']\".", "data-not-unique"); +} + +static void +test_unique_nested(void **state) +{ + struct lyd_node *tree; + + UTEST_ADD_MODULE(schema_d, LYS_IN_YANG, NULL, NULL); + + /* nested list uniquest are compared only with instances in the same parent list instance */ + LYD_TREE_CREATE("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>2</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>1</l3>\n" + " </lt3>\n" + " <lt3>\n" + " <kk>val2</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>3</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>4</l2>\n" + " </cont>\n" + " <l4>4</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <l4>5</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>1</l3>\n" + " </lt3>\n" + " <lt3>\n" + " <kk>val2</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + " <lt3>\n" + " <kk>val3</kk>\n" + " <l3>1</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>2</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>4</l2>\n" + " </cont>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <lt3>\n" + " <kk>val1</kk>\n" + " <l3>3</l3>\n" + " </lt3>\n" + "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l3\" not satisfied in \"/d:lt2[k='val2']/lt3[kk='val3']\" and " + "\"/d:lt2[k='val2']/lt3[kk='val1']\".", + "Data location \"/d:lt2[k='val2']/lt3[kk='val1']\".", "data-not-unique"); + + CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>2</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>3</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>2</l4>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <l4>5</l4>\n" + "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"cont/l2 l4\" not satisfied in \"/d:lt2[k='val4']\" and \"/d:lt2[k='val2']\".", + "Data location \"/d:lt2[k='val2']\".", "data-not-unique"); + + CHECK_PARSE_LYD_PARAM("<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val1</k>\n" + " <cont>\n" + " <l2>1</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>1</l5>\n" + " <l6>1</l6>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val2</k>\n" + " <cont>\n" + " <l2>2</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>1</l5>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val3</k>\n" + " <cont>\n" + " <l2>3</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>3</l5>\n" + " <l6>3</l6>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val4</k>\n" + " <cont>\n" + " <l2>4</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l6>1</l6>\n" + "</lt2>\n" + "<lt2 xmlns=\"urn:tests:d\">\n" + " <k>val5</k>\n" + " <cont>\n" + " <l2>5</l2>\n" + " </cont>\n" + " <l4>1</l4>\n" + " <l5>3</l5>\n" + " <l6>3</l6>\n" + "</lt2>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Unique data leaf(s) \"l5 l6\" not satisfied in \"/d:lt2[k='val5']\" and \"/d:lt2[k='val3']\".", + "Data location \"/d:lt2[k='val3']\".", "data-not-unique"); +} + +static void +test_dup(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module e {\n" + " namespace urn:tests:e;\n" + " prefix e;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf-list l {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " list lt {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " container cont {\n" + " list lt {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + " leaf-list ll2 {\n" + " type enumeration {\n" + " enum one;\n" + " enum two;\n" + " }\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:e\">25</d><d xmlns=\"urn:tests:e\">50</d>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"d\".", "Data location \"/e:d\"."); + + CHECK_PARSE_LYD_PARAM("<lt xmlns=\"urn:tests:e\"><k>A</k></lt>" + "<lt xmlns=\"urn:tests:e\"><k>B</k></lt>" + "<lt xmlns=\"urn:tests:e\"><k>A</k></lt>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"lt\".", "Data location \"/e:lt[k='A']\"."); + + CHECK_PARSE_LYD_PARAM("<ll xmlns=\"urn:tests:e\">A</ll>" + "<ll xmlns=\"urn:tests:e\">B</ll>" + "<ll xmlns=\"urn:tests:e\">B</ll>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"ll\".", "Data location \"/e:ll[.='B']\"."); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"></cont><cont xmlns=\"urn:tests:e\"/>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"cont\".", "Data location \"/e:cont\"."); + + /* same tests again but using hashes */ + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><d>25</d><d>50</d><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"d\".", "Data location \"/e:cont/d\", line number 1."); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>" + "<lt><k>a</k></lt>" + "<lt><k>b</k></lt>" + "<lt><k>c</k></lt>" + "<lt><k>d</k></lt>" + "<lt><k>c</k></lt></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"lt\".", "Data location \"/e:cont/lt[k='c']\", line number 1."); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>" + "<ll>a</ll><ll>b</ll><ll>c</ll><ll>d</ll><ll>d</ll></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"ll\".", "Data location \"/e:cont/ll[.='d']\", line number 1."); + + /* cases */ + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:e\">a</l>" + "<l xmlns=\"urn:tests:e\">b</l>" + "<l xmlns=\"urn:tests:e\">c</l>" + "<l xmlns=\"urn:tests:e\">b</l>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Duplicate instance of \"l\".", "Data location \"/e:l[.='b']\"."); + + CHECK_PARSE_LYD_PARAM("<l xmlns=\"urn:tests:e\">a</l><l xmlns=\"urn:tests:e\">b</l>" + "<l xmlns=\"urn:tests:e\">c</l>" + "<a xmlns=\"urn:tests:e\">aa</a>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Data for both cases \"a\" and \"b\" exist.", "Schema location \"/e:choic\"."); +} + +static void +test_defaults(void **state) +{ + struct lyd_node *tree, *node, *diff; + struct lys_module *mod; + const char *schema = + "module f {\n" + " namespace urn:tests:f;\n" + " prefix f;\n" + " yang-version 1.1;\n" + "\n" + " choice choic {\n" + " default \"c\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " case c {\n" + " leaf-list ll1 {\n" + " type string;\n" + " default \"def1\";\n" + " default \"def2\";\n" + " default \"def3\";\n" + " }\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " default 15;\n" + " }\n" + " leaf dd {\n" + " type uint32;\n" + " when '../d = 666';\n" + " default 15;\n" + " }\n" + " leaf-list ll2 {\n" + " type string;\n" + " default \"dflt1\";\n" + " default \"dflt2\";\n" + " }\n" + " container cont {\n" + " choice choic {\n" + " default \"c\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " case b {\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " case c {\n" + " leaf-list ll1 {\n" + " type string;\n" + " default \"def1\";\n" + " default \"def2\";\n" + " default \"def3\";\n" + " }\n" + " }\n" + " }\n" + " leaf d {\n" + " type uint32;\n" + " default 15;\n" + " }\n" + " leaf dd {\n" + " type uint32;\n" + " when '../d = 666';\n" + " default 15;\n" + " }\n" + " leaf-list ll2 {\n" + " type string;\n" + " default \"dflt1\";\n" + " default \"dflt2\";\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod); + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-with-defaults", "2011-06-01", NULL));\ + + /* get defaults */ + tree = NULL; + assert_int_equal(lyd_validate_module(&tree, mod, 0, &diff), LY_SUCCESS); + assert_non_null(tree); + assert_non_null(diff); + + /* check all defaults exist */ + CHECK_LYD_STRING_PARAM(tree, + "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">def3</ll1>\n" + "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"create\">\n" + " <ll1 yang:operation=\"create\">def1</ll1>\n" + " <ll1 yang:operation=\"create\">def2</ll1>\n" + " <ll1 yang:operation=\"create\">def3</ll1>\n" + " <d yang:operation=\"create\">15</d>\n" + " <ll2 yang:operation=\"create\">dflt1</ll2>\n" + " <ll2 yang:operation=\"create\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + + /* create another explicit case and validate */ + assert_int_equal(lyd_new_term(NULL, mod, "l", "value", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">def3</ll1>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + + /* create explicit leaf-list and leaf and validate */ + assert_int_equal(lyd_new_term(NULL, mod, "d", "15", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_new_term(NULL, mod, "ll2", "dflt2", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<d xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"delete\">dflt2</ll2>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + + /* create first explicit container, which should become implicit */ + assert_int_equal(lyd_new_inner(NULL, mod, "cont", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + /* check diff */ + assert_null(diff); + + /* create second explicit container, which should become implicit, so the first tree node should be removed */ + assert_int_equal(lyd_new_inner(NULL, mod, "cont", 0, &node), LY_SUCCESS); + assert_int_equal(lyd_insert_sibling(tree, node, &tree), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>\n" + " <ll1 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>\n" + " <d xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>\n" + " <ll2 xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_IMPL_TAG | LYD_PRINT_WITHSIBLINGS); + /* check diff */ + assert_null(diff); + + /* similar changes for nested defaults */ + assert_int_equal(lyd_new_term(tree->prev, NULL, "ll1", "def3", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(tree->prev, NULL, "d", "5", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_new_term(tree->prev, NULL, "ll2", "non-dflt", 0, NULL), LY_SUCCESS); + assert_int_equal(lyd_validate_all(&tree, UTEST_LYCTX, LYD_VALIDATE_PRESENT, &diff), LY_SUCCESS); + + /* check data tree */ + CHECK_LYD_STRING_PARAM(tree, + "<l xmlns=\"urn:tests:f\">value</l>\n" + "<d xmlns=\"urn:tests:f\">15</d>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1>def3</ll1>\n" + " <d>5</d>\n" + " <ll2>non-dflt</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WITHSIBLINGS); + + /* check diff */ + CHECK_LYD_STRING_PARAM(diff, + "<cont xmlns=\"urn:tests:f\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n" + " <ll1 yang:operation=\"delete\">def1</ll1>\n" + " <ll1 yang:operation=\"delete\">def2</ll1>\n" + " <ll1 yang:operation=\"delete\">def3</ll1>\n" + " <d yang:operation=\"delete\">15</d>\n" + " <ll2 yang:operation=\"delete\">dflt1</ll2>\n" + " <ll2 yang:operation=\"delete\">dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(diff); + lyd_free_all(tree); + + /* check data tree - when enabled node */ + CHECK_PARSE_LYD_PARAM("<d xmlns=\"urn:tests:f\">666</d><cont xmlns=\"urn:tests:f\"><d>666</d></cont>", + LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + CHECK_LYD_STRING_PARAM(tree, + "<ll1 xmlns=\"urn:tests:f\">def1</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\">def2</ll1>\n" + "<ll1 xmlns=\"urn:tests:f\">def3</ll1>\n" + "<d xmlns=\"urn:tests:f\">666</d>\n" + "<dd xmlns=\"urn:tests:f\">15</dd>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt1</ll2>\n" + "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>\n" + "<cont xmlns=\"urn:tests:f\">\n" + " <ll1>def1</ll1>\n" + " <ll1>def2</ll1>\n" + " <ll1>def3</ll1>\n" + " <d>666</d>\n" + " <dd>15</dd>\n" + " <ll2>dflt1</ll2>\n" + " <ll2>dflt2</ll2>\n" + "</cont>\n", + LYD_XML, LYD_PRINT_WD_ALL | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); +} + +static void +test_state(void **state) +{ + const char *data; + struct lyd_node *tree; + const char *schema = + "module h {\n" + " namespace urn:tests:h;\n" + " prefix h;\n" + " yang-version 1.1;\n" + "\n" + " container cont {\n" + " container cont2 {\n" + " config false;\n" + " leaf l {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + data = "<cont xmlns=\"urn:tests:h\">\n" + " <cont2>\n" + " <l>val</l>\n" + " </cont2>\n" + "</cont>\n"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_NO_STATE, 0, LY_EVALID, tree); + CHECK_LOG_CTX("Unexpected data state node \"cont2\" found.", + "Data location \"/h:cont/cont2\", line number 3."); + + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_NO_STATE, NULL)); + CHECK_LOG_CTX("Unexpected data state node \"cont2\" found.", + "Data location \"/h:cont/cont2\"."); + lyd_free_all(tree); +} + +static void +test_must(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module i {\n" + " namespace urn:tests:i;\n" + " prefix i;\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" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:i\">\n" + " <l>wrong</l>\n" + " <l2>val</l2>\n" + "</cont>\n", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", + "Data location \"/i:cont/l2\".", "must-violation"); + + LYD_TREE_CREATE("<cont xmlns=\"urn:tests:i\">\n" + " <l>right</l>\n" + " <l2>val</l2>\n" + "</cont>\n", tree); + lyd_free_all(tree); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:i\">\n" + " <l>wrong</l>\n" + " <l3>val</l3>\n" + "</cont>\n", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/i:cont/l3\".", "not-left"); +} + +const char *schema_j = + "module j {\n" + " namespace urn:tests:j;\n" + " prefix j;\n" + " yang-version 1.1;\n" + "\n" + " feature feat1;\n" + "\n" + " container cont {\n" + " must \"false()\";\n" + " list l1 {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " if-feature feat1;\n" + " input {\n" + " must \"../../lf1 = 'true'\";\n" + " leaf lf2 {\n" + " type leafref {\n" + " path /lf3;\n" + " }\n" + " }\n" + " }\n" + " output {\n" + " must \"../../lf1 = 'true2'\";\n" + " leaf lf2 {\n" + " type leafref {\n" + " path /lf4;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " leaf lf1 {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " leaf lf3 {\n" + " type string;\n" + " }\n" + "\n" + " leaf lf4 {\n" + " type string;\n" + " }\n" + "}"; +const char *feats_j[] = {"feat1", NULL}; + +static void +test_action(void **state) +{ + struct ly_in *in; + struct lyd_node *tree, *op_tree; + + UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory( + "<cont xmlns=\"urn:tests:j\">\n" + " <l1>\n" + " <k>val1</k>\n" + " <act>\n" + " <lf2>target</lf2>\n" + " </act>\n" + " </l1>\n" + "</cont>\n", &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &op_tree, NULL)); + assert_non_null(op_tree); + + /* missing leafref */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_RPC_YANG, NULL)); + CHECK_LOG_CTX("Invalid leafref value \"target\" - no target instance \"/lf3\" with the same value.", + "Data location \"/j:cont/l1[k='val1']/act/lf2\"."); + ly_in_free(in, 0); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>not true</lf1>\n" + "</cont>\n" + "<lf3 xmlns=\"urn:tests:j\">target</lf3>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* input must false */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_RPC_YANG, NULL)); + CHECK_LOG_CTX("Must condition \"../../lf1 = 'true'\" not satisfied.", + "Data location \"/j:cont/l1[k='val1']/act\"."); + + lyd_free_all(tree); + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>true</lf1>\n" + "</cont>\n" + "<lf3 xmlns=\"urn:tests:j\">target</lf3>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* success */ + assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_RPC_YANG, NULL)); + + lyd_free_tree(op_tree); + lyd_free_siblings(tree); +} + +static void +test_rpc(void **state) +{ + const char *schema, *data; + struct ly_in *in; + struct lyd_node *tree; + + /* Testing constraint violation in RPC. */ + schema = + "module val-str {\n" + " namespace \"urn:vstr\";\n" + " prefix v;\n" + "\n" + " rpc modify-user-password {\n" + " input {\n" + " leaf old-password {\n" + " type string {\n" + " length \"4..8\";\n" + " }\n" + " }\n" + " leaf new-password {\n" + " type string {\n" + " length \"4..8\";\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + data = + "<modify-user-password xmlns=\"urn:vstr\">\n" + " <old-password>12345</old-password>\n" + " <new-password>123</new-password>\n" + "</modify-user-password>"; + 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_YANG, &tree, NULL)); + CHECK_LOG_CTX("Unsatisfied length - string \"123\" length is not allowed.", + "Data location \"/val-str:modify-user-password/new-password\", line number 3."); + ly_in_free(in, 0); +} + +static void +test_reply(void **state) +{ + struct ly_in *in; + struct lyd_node *tree, *op_tree; + + UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory( + "<cont xmlns=\"urn:tests:j\">\n" + " <l1>\n" + " <k>val1</k>\n" + " <act>\n" + " <lf2>target</lf2>\n" + " </act>\n" + " </l1>\n" + "</cont>\n", &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &op_tree, NULL)); + assert_non_null(op_tree); + ly_in_free(in, 0); + + /* missing leafref */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_REPLY_YANG, NULL)); + CHECK_LOG_CTX("Invalid leafref value \"target\" - no target instance \"/lf4\" with the same value.", + "Data location \"/j:cont/l1[k='val1']/act/lf2\"."); + + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>not true</lf1>\n" + "</cont>\n" + "<lf4 xmlns=\"urn:tests:j\">target</lf4>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* input must false */ + assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_REPLY_YANG, NULL)); + CHECK_LOG_CTX("Must condition \"../../lf1 = 'true2'\" not satisfied.", "Data location \"/j:cont/l1[k='val1']/act\"."); + + lyd_free_all(tree); + CHECK_PARSE_LYD_PARAM("<cont xmlns=\"urn:tests:j\">\n" + " <lf1>true2</lf1>\n" + "</cont>\n" + "<lf4 xmlns=\"urn:tests:j\">target</lf4>\n", + LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree); + + /* success */ + assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_REPLY_YANG, NULL)); + + lyd_free_tree(op_tree); + lyd_free_all(tree); +} + +static void +test_case(void **state) +{ + struct lyd_node *tree; + const char *schema = + "module k {\n" + " namespace urn:tests:k;\n" + " prefix k;\n" + " yang-version 1.1;\n" + "\n" + " container ch {\n" + " choice a0 {\n" + " case v0 {\n" + " leaf g0 {\n" + " type string;\n" + " }\n" + " }\n" + " case v1 {\n" + " choice a1 {\n" + " case r0 {\n" + " leaf g1 {\n" + " type string;\n" + " }\n" + " }\n" + " case r1 {\n" + " leaf g2 {\n" + " type string;\n" + " }\n" + " leaf g3 {\n" + " type string;\n" + " }\n" + " }\n" + " case r2 {\n" + " leaf g4 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " case v2 {\n" + " choice a2 {\n" + " case y0 {\n" + " leaf g5 {\n" + " type string;\n" + " }\n" + " }\n" + " case y1 {\n" + " leaf g6 {\n" + " type string;\n" + " }\n" + " leaf g7 {\n" + " type string;\n" + " }\n" + " }\n" + " case y2 {\n" + " leaf g8 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + + UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); + + CHECK_PARSE_LYD_PARAM( + "{\n" + " \"k:ch\": {\n" + " \"g0\": \"value_g0\",\n" + " \"g7\": \"value_g7\"\n" + " }\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."); + + CHECK_PARSE_LYD_PARAM( + "{\n" + " \"k:ch\": {\n" + " \"g7\": \"value_g7\",\n" + " \"g0\": \"value_g0\"\n" + " }\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."); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_when), + UTEST(test_mandatory), + UTEST(test_mandatory_when), + UTEST(test_type_incomplete_when), + UTEST(test_minmax), + UTEST(test_unique), + UTEST(test_unique_nested), + UTEST(test_dup), + UTEST(test_defaults), + UTEST(test_state), + UTEST(test_must), + UTEST(test_action), + UTEST(test_rpc), + UTEST(test_reply), + UTEST(test_case), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} |