/** * @file test_diff.c * @author Radek Krejci * @author Michal Vasko * @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 = "42"; 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 = "\n" " true\n" " permit\n" " deny\n" " permit\n" " true\n" "\n" " 4242\n" "\n" " 4242\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 = "\n" " 42\n" " 42\n" "\n" "\n" " 42\n" " 42\n" "\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, "\n" " 42\n" " 42\n" "\n" "\n" " 42\n" " 42\n" "\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 = "\n" " 42\n" " 42\n" "\n" " 42\n" " 42\n" "\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, "\n" " 42\n" " 42\n" "\n" "\n" " 42\n" " 42\n" "\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 = "42"; 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, "\n" " 42\n" "\n"); struct lyd_node *diff2; CHECK_PARSE_LYD_DIFF(lyd_child(model_1), NULL, diff2); CHECK_LYD_STRING(diff2, "\n" " 42\n" "\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 = "\n" " \n" " a\n" " \n" " a\n" " \n" " \n" "\n"; const char *xml2 = "\n" " \n" " a\n" " \n" "\n"; const char *xml_merge = "\n" " \n" " a\n" " \n" " a\n" " \n" " \n" "\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 = "\n" " 42\n" "\n" "\n" " 42\n" " 42\n" "\n"; const char *xml2 = "\n" " 41\n" " 42\n" "\n"; const char *xml3 = "\n" " 40\n" "\n" "\n" " 40\n" "\n"; const char *out_diff_1 = "\n" " 41\n" " 42\n" "\n" "\n" " 42\n" " 42\n" "\n"; const char *out_diff_2 = "\n" " 40\n" " 42\n" "\n" "\n" " 40\n" "\n"; const char *out_merge = "\n" " 40\n" "\n" "\n" " 40\n" " 42\n" "\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 = "\n" " \n" " a\n" " 1\n" " \n" " \n" " b\n" " 2\n" " \n" "\n"; const char *xml2 = "\n" " \n" " b\n" " -2\n" " \n" " \n" " c\n" " 3\n" " \n" "\n"; const char *xml3 = "\n" " \n" " b\n" " -2\n" " \n" " \n" " a\n" " 2\n" " \n" "\n"; const char *out_diff_1 = "\n" " \n" " a\n" " 1\n" " \n" " \n" " b\n" " -2\n" " \n" " \n" " c\n" " 3\n" " \n" "\n"; const char *out_diff_2 = "\n" " \n" " c\n" " 3\n" " \n" " \n" " a\n" " 2\n" " \n" "\n"; const char *out_merge = "\n" " \n" " a\n" " 2\n" " \n" " \n" " b\n" " -2\n" " \n" "\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 = "\n" " 1\n" " 2\n" " 3\n" " 4\n" " 5\n" "\n"; const char *xml2 = "\n" " 1\n" " 4\n" " 3\n" " 2\n" " 5\n" "\n"; const char *xml3 = "\n" " 5\n" " 4\n" " 3\n" " 2\n" "\n"; const char *out_diff_1 = "\n" " 4\n" " 3\n" "\n"; const char *out_diff_2 = "\n" " 1\n" " 5\n" "\n"; const char *out_merge = "\n" " 4\n" " 3\n" " 1\n" " 5\n" "\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 = "\n" " 1\n" " a1\n" " 2\n" " 3\n" " 4\n" "\n"; const char *xml2 = "\n" " 1\n" " a1\n" " 2\n" " 4\n" " 3\n" "\n"; const char *xml3 = "\n" " 4\n" " 1\n" " a1\n" " 3\n" "\n"; const char *out_diff_1 = "\n" " 4\n" "\n"; const char *out_diff_2 = "\n" " 2\n" " 4\n" "\n"; const char *out_merge = "\n" " 4\n" " 2\n" "\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 = "\n" " 1\n" " 2\n" " 3\n" "\n"; const char *xml2 = "\n" " 3\n" " 1\n" "\n"; const char *xml3 = "\n" " 1\n" " 4\n" " 3\n" "\n"; const char *out_diff_1 = "\n" " 2\n" " 3\n" "\n"; const char *out_diff_2 = "\n" " 1\n" " 4\n" "\n"; const char *out_merge = "\n" " 2\n" " 3\n" " 1\n" " 4\n" "\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 = "\n" "
    \n" " a\n" " 1\n" "
\n" "
    \n" " b\n" " 2\n" "
\n" "
    \n" " c\n" " 3\n" "
\n" "
\n"; const char *xml2 = "\n" "
    \n" " a\n" " 11\n" "
\n" "
    \n" " c\n" " 3\n" "
\n" "
\n"; const char *xml3 = "\n" "
    \n" " c\n" " 33\n" "
\n" "
    \n" " b\n" " 2\n" "
\n" "
\n"; const char *out_diff_1 = "\n" "
    \n" " a\n" " 11\n" "
\n" "
    \n" " b\n" " 2\n" "
\n" "
\n"; const char *out_diff_2 = "\n" "
    \n" " a\n" " 11\n" "
\n" "
    \n" " c\n" " 33\n" "
\n" "
    \n" " b\n" " 2\n" "
\n" "
\n"; const char *out_merge = "\n" "
    \n" " a\n" " 1\n" "
\n" "
    \n" " b\n" "
\n" "
    \n" " c\n" " 33\n" "
\n" "
\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 = "\n" "
    \n" " d\n" " 4\n" "
\n" "
\n"; const char *xml2 = "\n" "
    \n" " c\n" " 3\n" "
\n" "
    \n" " d\n" " 4\n" "
\n" "
\n"; const char *xml3 = "\n" "
    \n" " a\n" " 1\n" "
\n" "
    \n" " b\n" " 2\n" "
\n" "
    \n" " c\n" " 3\n" "
\n" "
    \n" " d\n" " 4\n" "
\n" "
\n"; const char *out_diff_1 = "\n" "
    \n" " c\n" " 3\n" "
\n" "
\n"; const char *out_diff_2 = "\n" "
    \n" " a\n" " 1\n" "
\n" "
    \n" " b\n" " 2\n" "
\n" "
\n"; const char *out_merge = "\n" "
    \n" " c\n" " 3\n" "
\n" "
    \n" " a\n" " 1\n" "
\n" "
    \n" " b\n" " 2\n" "
\n" "
\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 = "\n" " \n" " a\n" " 1\n" " \n" " \n" " b\n" " 2\n" " \n" " \n" " c\n" " 3\n" " \n" "\n"; const char *xml2 = "\n" " \n" " b\n" " 2\n" " \n" " \n" " a\n" " 1\n" " \n" " \n" " a\n" " 1\n" " \n" "\n"; const char *xml3 = "\n" " \n" " c\n" " \n" " \n" " 4\n" " \n" " \n" " e\n" " 5\n" " \n" " \n" " f\n" " 6\n" " \n" "\n"; const char *out_diff_1 = "\n" " \n" " c\n" " 3\n" " \n" " \n" " b\n" " 2\n" " \n" " \n" " a\n" " 1\n" " \n" "\n"; const char *out_diff_2 = "\n" " \n" " b\n" " 2\n" " \n" " \n" " a\n" " 1\n" " \n" " \n" " a\n" " 1\n" " \n" " \n" " c\n" " \n" " \n" " 4\n" " \n" " \n" " e\n" " 5\n" " \n" " \n" " f\n" " 6\n" " \n" "\n"; const char *out_merge = "\n" " \n" " c\n" " 3\n" " \n" " \n" " b\n" " 2\n" " \n" " \n" " a\n" " 1\n" " \n" " \n" " c\n" " \n" " \n" " 4\n" " \n" " \n" " e\n" " 5\n" " \n" " \n" " f\n" " 6\n" " \n" "\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 = "\n" " a\n" " b\n" " c\n" "\n"; const char *xml2 = "\n" " b\n" " c\n" " a\n" " a\n" " a\n" "\n"; const char *xml3 = "\n" " a\n" " d\n" " a\n" "\n"; const char *out_diff_1 = "\n" " b\n" " c\n" " a\n" " a\n" "\n"; const char *out_diff_2 = "\n" " b\n" " c\n" " a\n" " d\n" "\n"; const char *out_merge = "\n" " b\n" " c\n" " a\n" " d\n" "\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 = "\n" " 41\n" " 4\n" "\n"; const char *xml3 = "\n" " 42\n" " 4\n" " 1\n" "\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 = "\n" " 41\n" " 1\n" " 2\n" " 3\n" " 4\n" "\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, "\n" " 42\n" " 1\n" "\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 = "\n" " 42\n" " 1\n" " 2\n" " 3\n" " 4\n" "\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); }