/** * @file test_merge.c * @author Michal Vasko * @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 = "\n" " \n" " yang\n" " 2016-02-11\n" " implement\n" " \n" "\n"; const char *data[] = { "\n" " \n" " ietf-yang-library\n" " 2016-02-01\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf-acm\n" " 2012-02-22\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf\n" " 2011-06-01\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf-monitoring\n" " 2010-10-04\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf-with-defaults\n" " 2011-06-01\n" " implement\n" " \n" "\n", "\n" " \n" " yang\n" " 2016-02-11\n" " urn:ietf:params:xml:ns:yang:1\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-yang-library\n" " 2016-02-01\n" " urn:ietf:params:xml:ns:yang:ietf-yang-library\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf-acm\n" " 2012-02-22\n" " urn:ietf:params:xml:ns:yang:ietf-netconf-acm\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf\n" " 2011-06-01\n" " urn:ietf:params:xml:ns:netconf:base:1.0\n" " writable-running\n" " candidate\n" " rollback-on-error\n" " validate\n" " startup\n" " xpath\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf-monitoring\n" " 2010-10-04\n" " urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\n" " implement\n" " \n" "\n", "\n" " \n" " ietf-netconf-with-defaults\n" " 2011-06-01\n" " urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\n" " implement\n" " \n" "\n" }; const char *output_template = "\n" " \n" " yang\n" " 2016-02-11\n" " urn:ietf:params:xml:ns:yang:1\n" " implement\n" " \n" " \n" " ietf-yang-library\n" " 2016-02-01\n" " urn:ietf:params:xml:ns:yang:ietf-yang-library\n" " implement\n" " \n" " \n" " ietf-netconf-acm\n" " 2012-02-22\n" " urn:ietf:params:xml:ns:yang:ietf-netconf-acm\n" " implement\n" " \n" " \n" " ietf-netconf\n" " 2011-06-01\n" " urn:ietf:params:xml:ns:netconf:base:1.0\n" " writable-running\n" " candidate\n" " rollback-on-error\n" " validate\n" " startup\n" " xpath\n" " implement\n" " \n" " \n" " ietf-netconf-monitoring\n" " 2010-10-04\n" " urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\n" " implement\n" " \n" " \n" " ietf-netconf-with-defaults\n" " 2011-06-01\n" " urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\n" " implement\n" " \n" "\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 = " block "; const char *src = " aa bb "; const char *result = "aabb"; 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 = " aaa "; const char *src = " bbb "; const char *result = "aaabbb"; 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 = "\n" " \n" " 1\n" " a\n" " true\n" " \n" "\n"; const char *src = "\n" " \n" " 1\n" " b\n" " \n" "\n"; const char *result = "\n" " \n" " 1\n" " b\n" " true\n" " \n" "\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 = "\n" " \n" " 1\n" " a\n" " \n" " val\n" " \n" " \n" "\n"; const char *src = "\n" " \n" " 1\n" " b\n" " \n" "\n"; const char *result = "\n" " \n" " 1\n" " b\n" " \n" " val\n" " \n" " \n" "\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 = "\n" " \n" " 1\n" " b\n" " \n" " \n" " 1\n" " a\n" " \n" " val\n" " \n" " \n" "\n"; const char *src = "\n" " \n" " 1\n" " b\n" " \n" " \n" " 2\n" " a\n" " \n" "\n"; const char *result = "\n" " \n" " 1\n" " b\n" " \n" " \n" " 1\n" " a\n" " \n" " val\n" " \n" " \n" " \n" " 2\n" " a\n" " \n" "\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 = "\n" " a\n" " b\n" " c\n" " d\n" " a\n" " b\n" " c\n" " d\n" "\n"; const char *src = "\n" " d\n" " c\n" " b\n" " a\n" " a\n" " a\n" " a\n" " f\n" " f\n" "\n"; const char *result = "\n" " a\n" " b\n" " c\n" " d\n" " a\n" " b\n" " c\n" " d\n" " a\n" " a\n" " f\n" " f\n" "\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 = "\n" " \n" " 1\n" " \n" "\n"; const char *src = "\n" " 1\n" "\n"; const char *result = "\n" " 1\n" "\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 = "a" "ba"; const char *src = "ca" "a*"; const char *res = "a*" "ba" "ca"; 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); }