diff options
Diffstat (limited to 'tests/utests/schema')
-rw-r--r-- | tests/utests/schema/test_printer_tree.c | 2522 | ||||
-rw-r--r-- | tests/utests/schema/test_schema.c | 1915 | ||||
-rw-r--r-- | tests/utests/schema/test_tree_schema_compile.c | 4110 | ||||
-rw-r--r-- | tests/utests/schema/test_yang.c | 1758 | ||||
-rw-r--r-- | tests/utests/schema/test_yin.c | 3589 |
5 files changed, 13894 insertions, 0 deletions
diff --git a/tests/utests/schema/test_printer_tree.c b/tests/utests/schema/test_printer_tree.c new file mode 100644 index 0000000..5438c61 --- /dev/null +++ b/tests/utests/schema/test_printer_tree.c @@ -0,0 +1,2522 @@ +/* + * @file test_printer_tree.c + * @author: Adam Piecek <piecek@cesnet.cz> + * @brief unit tests for functions from printer_tree.c + * + * Copyright (c) 2019-2021 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 "ly_common.h" +#include "out.h" +#include "printer_schema.h" +#include "tree_schema.h" + +#define TEST_LOCAL_SETUP \ + char *printed; \ + struct lys_module *mod; \ + const char *orig; \ + const char *expect; \ + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &UTEST_OUT)); + +#define TEST_LOCAL_PRINT(MOD, LINE_LENGTH) \ + assert_int_equal(LY_SUCCESS, lys_print_module(UTEST_OUT, MOD, LYS_OUT_TREE, LINE_LENGTH, 0)); + +#define TEST_LOCAL_TEARDOWN \ + ly_out_free(UTEST_OUT, NULL, 1); + +static void +base_sections(void **state) +{ + TEST_LOCAL_SETUP; + struct lys_module *modxx; + + orig = + "module a01xx {\n" + " yang-version 1.1;\n" + " namespace \"xx:y\";\n" + " prefix xx;\n" + " container c;\n" + " container d;\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &modxx); + + /* module with import statement */ + orig = + "module a01 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " import a01xx {\n" + " prefix xx;\n" + " }\n" + "\n" + " grouping g1;\n" + "\n" + " grouping g2;\n" + " container g;\n" + " augment \"/xx:c\" {\n" + " container e;\n" + " }\n" + " augment \"/xx:d\" {\n" + " container f;\n" + " }\n" + " rpc rpc1;\n" + " rpc rpc2;\n" + " notification n1;\n" + " notification n2;\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* from pyang */ + expect = + "module: a01\n" + " +--rw g\n" + "\n" + " augment /xx:c:\n" + " +--rw e\n" + " augment /xx:d:\n" + " +--rw f\n" + "\n" + " rpcs:\n" + " +---x rpc1\n" + " +---x rpc2\n" + "\n" + " notifications:\n" + " +---n n1\n" + " +---n n2\n" + "\n" + " grouping g1\n" + " grouping g2\n"; + + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang */ + expect = + "module: a01\n" + " +--rw g\n" + "\n" + " augment /xx:c:\n" + " +--rw e\n" + " augment /xx:d:\n" + " +--rw f\n" + "\n" + " rpcs:\n" + " +---x rpc1\n" + " +---x rpc2\n" + "\n" + " notifications:\n" + " +---n n1\n" + " +---n n2\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang */ + expect = + "module: a01xx\n" + " +--rw c\n" + " | +--rw x:e\n" + " +--rw d\n" + " +--rw x:f\n"; + + TEST_LOCAL_PRINT(modxx, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_status(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a02 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container l {\n" + " status current;\n" + " }\n" + " container m {\n" + " status deprecated;\n" + " }\n" + " container n {\n" + " status obsolete;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a02\n" + " +--rw l\n" + " x--rw m\n" + " o--rw n\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_config_flags(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a03 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container l {\n" + " config true;\n" + " }\n" + " container m {\n" + " config false;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a03\n" + " +--rw l\n" + " +--ro m\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_rpcs_flags(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a04 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container cont {\n" + " action rpc1 {\n" + "\n" + " input {\n" + " leaf in {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " output {\n" + " leaf out {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a04\n" + " +--rw cont\n" + " +---x rpc1\n" + " +---w input\n" + " | +---w in? string\n" + " +--ro output\n" + " +--ro out? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_grouping_flags(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a05 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " grouping g {\n" + " leaf a {\n" + " type string;\n" + " config true;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " config false;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " container d {\n" + " config false;\n" + " leaf e {\n" + " type string;\n" + " }\n" + " }\n" + " container f {\n" + " leaf g {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container d {\n" + " uses g;\n" + " }\n" + "}\n"; + + /* from yanglint1 */ + expect = + "module: a05\n" + " +--rw d\n" + " +---u g\n" + "\n" + " grouping g:\n" + " +--rw a? string\n" + " +--ro b? string\n" + " +---- c? string\n" + " +--ro d\n" + " | +--ro e? string\n" + " +---- f\n" + " +---- g? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + /* from pyang */ + expect = + "module: a05\n" + " +--rw d\n" + " +--rw a? string\n" + " +--ro b? string\n" + " +--rw c? string\n" + " +--ro d\n" + " | +--ro e? string\n" + " +--rw f\n" + " +--rw g? string\n"; + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +notif_inside_container(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a06 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container c {\n" + " notification notif;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a06\n" + " +--rw c\n" + " +---n notif\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_choice(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a07 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " choice my_choice;\n" + "}\n"; + + /* from pyang */ + expect = + "module: a07\n" + " +--rw (my_choice)?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_case(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a08 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature foo;\n" + " choice my_choice {\n" + " case my_case;\n" + " }\n" + " choice shorthand {\n" + " container cont1 {\n" + " if-feature \"foo\";\n" + " status obsolete;\n" + " }\n" + " container cont2 {\n" + " container cont3;\n" + " }\n" + " }\n" + " container top {\n" + " choice shorthand1 {\n" + " container cont1;\n" + " }\n" + " choice shorthand2 {\n" + " container cont2 {\n" + " container cont3;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a08\n" + " +--rw (my_choice)?\n" + " | +--:(my_case)\n" + " +--rw (shorthand)?\n" + " | o--:(cont1)\n" + " | | o--rw cont1 {foo}?\n" + " | +--:(cont2)\n" + " | +--rw cont2\n" + " | +--rw cont3\n" + " +--rw top\n" + " +--rw (shorthand1)?\n" + " | +--:(cont1)\n" + " | +--rw cont1\n" + " +--rw (shorthand2)?\n" + " +--:(cont2)\n" + " +--rw cont2\n" + " +--rw cont3\n"; + + const char *feats[] = {"foo", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +optional_opts(void **state) +{ + TEST_LOCAL_SETUP; + /* throws libyang warn: Use of anydata to define configuration data is not recommended... */ + orig = + "module a09 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf l1 {\n" + " type string;\n" + " mandatory true;\n" + " }\n" + " leaf l2 {\n" + " type string;\n" + " mandatory false;\n" + " }\n" + " choice c1 {\n" + " mandatory true;\n" + " }\n" + " choice c2 {\n" + " mandatory false;\n" + " }\n" + " anydata a1 {\n" + " mandatory true;\n" + " }\n" + " anydata a2 {\n" + " mandatory false;\n" + " }\n" + " anyxml x1 {\n" + " mandatory true;\n" + " }\n" + " anyxml x2 {\n" + " mandatory false;\n" + " }\n" + "}\n"; + + /* from yanglint 1 */ + expect = + "module: a09\n" + " +--rw l1 string\n" + " +--rw l2? string\n" + " +--rw (c1)\n" + " +--rw (c2)?\n" + " +--rw a1 anydata\n" + " +--rw a2? anydata\n" + " +--rw x1 anyxml\n" + " +--rw x2? anyxml\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +presence_container(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a10 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container c;\n" + " container d {\n" + " presence \"str1\";\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a10\n" + " +--rw c\n" + " +--rw d!\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_keys(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a11 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " list l1 {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " }\n" + " list l2 {\n" + " key \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " leaf-list ll {\n" + " type string;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a11\n" + " +--rw l1* [a]\n" + " | +--rw a string\n" + " +--rw l2* [a b]\n" + " | +--rw a string\n" + " | +--rw b string\n" + " +--rw ll* string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_type_target(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a12 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf a {\n" + " type leafref {\n" + " path \"/x:b\";\n" + " }\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + "}\n"; + + /* from yanglint 1 */ + expect = + "module: a12\n" + " +--rw a? -> /x:b\n" + " +--rw b? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_type_leafref(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a13 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters {\n" + " type string;\n" + " }\n" + " leaf a {\n" + " type leafref {\n" + " path \"/x:pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters\";\n" + " }\n" + " }\n" + "}\n"; + + /* yanglint --tree-no-leafref-target --tree-line-length=72 */ + expect = + "module: a13\n" + " +--rw pretty-long-identifier-name-which-should-exceed-the-limit-of-72-characters?\n" + " | string\n" + " +--rw a?\n" + " leafref\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +node_iffeatures(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a14 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature foo;\n" + "\n" + " feature bar;\n" + " container c {\n" + " if-feature \"foo or bar\";\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a14\n" + " +--rw c {foo or bar}?\n"; + + const char *feats[] = {"foo", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +indent_wrapper(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a15 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container a {\n" + " container b;\n" + " }\n" + " container c {\n" + " container d {\n" + " container e;\n" + " }\n" + " container f {\n" + " container g;\n" + " }\n" + " }\n" + " container h;\n" + " container i {\n" + " container j;\n" + " container k;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a15\n" + " +--rw a\n" + " | +--rw b\n" + " +--rw c\n" + " | +--rw d\n" + " | | +--rw e\n" + " | +--rw f\n" + " | +--rw g\n" + " +--rw h\n" + " +--rw i\n" + " +--rw j\n" + " +--rw k\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +line_length_twiddling(void **state) +{ + TEST_LOCAL_SETUP; + /* node_fits_tight */ + + orig = + "module a16 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature f;\n" + "\n" + " typedef some-long-type {\n" + " type string;\n" + " }\n" + " list my-list-name {\n" + " key \"key\";\n" + " leaf key {\n" + " type string;\n" + " }\n" + " leaf nod-leaf {\n" + " if-feature \"f\";\n" + " type some-long-type;\n" + " }\n" + " leaf nos-leaf {\n" + " if-feature \"f\";\n" + " type int32;\n" + " }\n" + " }\n" + "}\n"; + + /* pyang --tree-line-length 42 */ + expect = + "module: a16\n" + " +--rw my-list-name* [key]\n" + " +--rw key string\n" + " +--rw nod-leaf? some-long-type {f}?\n" + " +--rw nos-leaf? int32 {f}?\n"; + + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 42); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 42); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* break_before_iffeature */ + + /* pyang --tree-line-length 41 */ + expect = + "module: a16\n" + " +--rw my-list-name* [key]\n" + " +--rw key string\n" + " +--rw nod-leaf? some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf? int32 {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 41); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 41); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* break_before_type */ + + /* pyang --tree-line-length 29 */ + expect = + "module: a16\n" + " +--rw my-list-name* [key]\n" + " +--rw key string\n" + " +--rw nod-leaf?\n" + " | some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf? int32\n" + " {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 29); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 29); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* break_before_keys */ + + /* pyang --tree-line-length 23 */ + expect = + "module: a16\n" + " +--rw my-list-name*\n" + " [key]\n" + " +--rw key\n" + " | string\n" + " +--rw nod-leaf?\n" + " | some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf?\n" + " int32 {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 23); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 23); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* every_node_name_is_too_long */ + + /* pyang --tree-line-length 14 */ + expect = + "module: a16\n" + " +--rw my-list-name*\n" + " [key]\n" + " +--rw key\n" + " | string\n" + " +--rw nod-leaf?\n" + " | some-long-type\n" + " | {f}?\n" + " +--rw nos-leaf?\n" + " int32\n" + " {f}?\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +break_before_leafref(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a17 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " leaf e {\n" + " type string;\n" + " }\n" + " leaf abcd {\n" + " type leafref {\n" + " path \"/x:e\";\n" + " }\n" + " }\n" + "}\n"; + + /* yanglint --tree-line-length 14 */ + expect = + "module: a17\n" + " +--rw e?\n" + " | string\n" + " +--rw abcd?\n" + " -> /x:e\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 14); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +break_before_leafref_and_iffeature(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a18 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " feature f;\n" + " leaf some-long-id {\n" + " type string;\n" + " }\n" + " leaf a {\n" + " if-feature \"f\";\n" + " type leafref {\n" + " path \"/x:some-long-id\";\n" + " }\n" + " }\n" + "}\n"; + + /* yanglint --tree-no-leafref-target --tree-line-length=20 */ + expect = + "module: a18\n" + " +--rw some-long-id?\n" + " | string\n" + " +--rw a?\n" + " leafref\n" + " {f}?\n"; + + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, feats, &mod); + TEST_LOCAL_PRINT(mod, 20); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 20); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +basic_unified_indent_before_type(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a19 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " typedef longType {\n" + " type string;\n" + " }\n" + " container A {\n" + " leaf Bnode {\n" + " type int8;\n" + " }\n" + " leaf Cnode {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf Dnode {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf E {\n" + " type longType;\n" + " mandatory true;\n" + " }\n" + " leaf G {\n" + " type int8;\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a19\n" + " +--rw A\n" + " +--rw Bnode? int8\n" + " +--rw Cnode int8\n" + " +--rw Dnode int8\n" + " +--rw E longType\n" + " +--rw G? int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +twiddling_unified_indent_before_type(void **state) +{ + TEST_LOCAL_SETUP; + /* basic_functionality */ + + orig = + "module a20 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " typedef longType {\n" + " type string;\n" + " }\n" + " container A {\n" + " leaf Bnode {\n" + " type int8;\n" + " }\n" + " leaf CnodeIsBigger {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf Dnode {\n" + " type int8;\n" + " mandatory true;\n" + " }\n" + " leaf E {\n" + " type longType;\n" + " mandatory true;\n" + " }\n" + " leaf G {\n" + " type int8;\n" + " }\n" + " }\n" + "}\n"; + + /* pyang --tree-line-length 36 */ + expect = + "module: a20\n" + " +--rw A\n" + " +--rw Bnode? int8\n" + " +--rw CnodeIsBigger int8\n" + " +--rw Dnode int8\n" + " +--rw E longType\n" + " +--rw G? int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 36); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 36); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* unified_indent_before_type_long_node_name */ + + /* pyang --tree-line-length 32 */ + expect = + "module: a20\n" + " +--rw A\n" + " +--rw Bnode? int8\n" + " +--rw CnodeIsBigger int8\n" + " +--rw Dnode int8\n" + " +--rw E\n" + " | longType\n" + " +--rw G? int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 32); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 32); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + ly_out_reset(UTEST_OUT); + /* unified_indent_before_type_long_node_type */ + + /* pyang --tree-line-length 31 */ + expect = + "module: a20\n" + " +--rw A\n" + " +--rw Bnode?\n" + " | int8\n" + " +--rw CnodeIsBigger\n" + " | int8\n" + " +--rw Dnode\n" + " | int8\n" + " +--rw E\n" + " | longType\n" + " +--rw G?\n" + " int8\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 31); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 31); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +inheritance_of_config_flag(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a21 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container a {\n" + " config false;\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a21\n" + " +--ro a\n" + " +--ro b? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +inheritance_of_status_flag(void **state) +{ + TEST_LOCAL_SETUP; + /* throws libyang warn: Missing explicit "..." status that was already specified in parent, inheriting. */ + orig = + "module a22 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container a {\n" + " status current;\n" + " container b {\n" + " status deprecated;\n" + " leaf f {\n" + " type string;\n" + " }\n" + " }\n" + " container c {\n" + " status obsolete;\n" + " leaf g {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " container d {\n" + " status deprecated;\n" + " container h {\n" + " status obsolete;\n" + " leaf e {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from yanglint 1 */ + expect = + "module: a22\n" + " +--rw a\n" + " | x--rw b\n" + " | | x--rw f? string\n" + " | o--rw c\n" + " | o--rw g? string\n" + " x--rw d\n" + " o--rw h\n" + " o--rw e? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +key_leaf_is_always_mandatory_true(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a23 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " list a {\n" + " key \"k1\";\n" + " list b {\n" + " key \"k2\";\n" + " leaf k1 {\n" + " type string;\n" + " }\n" + " leaf k2 {\n" + " type string;\n" + " }\n" + " }\n" + " leaf k1 {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a23\n" + " +--rw a* [k1]\n" + " +--rw b* [k2]\n" + " | +--rw k1? string\n" + " | +--rw k2 string\n" + " +--rw k1 string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang but with some swapped lines */ + expect = + "module: a23\n" + " +--rw a* [k1]\n" + " +--rw k1 string\n" + " +--rw b* [k2]\n" + " +--rw k2 string\n" + " +--rw k1? string\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +transition_between_rpc_and_notif(void **state) +{ + TEST_LOCAL_SETUP; + orig = + "module a24 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container top {\n" + " leaf g {\n" + " type string;\n" + " }\n" + " action rpc1 {\n" + "\n" + " input {\n" + " leaf in {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " action rpc2 {\n" + "\n" + " input {\n" + " leaf in {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " output {\n" + " leaf out {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " notification n1;\n" + " notification n2;\n" + " }\n" + "}\n"; + + /* from pyang */ + expect = + "module: a24\n" + " +--rw top\n" + " +--rw g? string\n" + " +---x rpc1\n" + " | +---w input\n" + " | +---w in? string\n" + " +---x rpc2\n" + " | +---w input\n" + " | | +---w in? string\n" + " | +--ro output\n" + " | +--ro out? string\n" + " +---n n1\n" + " +---n n2\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +local_augment(void **state) +{ + TEST_LOCAL_SETUP; + + orig = + "module a25 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + " container g;\n" + " augment \"/x:g\" {\n" + " container e;\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* from pyang */ + expect = + "module: a25\n" + " +--rw g\n" + " +--rw e\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +print_compiled_node(void **state) +{ + TEST_LOCAL_SETUP; + const struct lysc_node *node; + + orig = + "module a26 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " container g {\n" + " leaf a {\n" + " type string;\n" + " }\n" + " container h {\n" + " leaf b {\n" + " type string;\n" + " mandatory true;\n" + " }\n" + " leaf c {\n" + " type string;\n" + " }\n" + " list l {\n" + " key \"ip\";\n" + " leaf ip {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* pyang -f tree --tree-path /g/h/c */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n" + " +--rw c? string\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h/c", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* pyang -f tree --tree-path /g/h/l */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n" + " +--rw l* [ip]\n" + " +--rw ip string\n"; + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h/l", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* pyang -f tree --tree-path /g/h */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n" + " +--rw b string\n" + " +--rw c? string\n" + " +--rw l* [ip]\n" + " +--rw ip string\n"; + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* pyang whose output is adjusted manually */ + expect = + "module: a26\n" + " +--rw g\n" + " +--rw h\n"; + + node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, LYS_PRINT_NO_SUBSTMT)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +print_compiled_node_augment(void **state) +{ + TEST_LOCAL_SETUP; + const struct lysc_node *node; + + orig = + "module b26xx {\n" + " yang-version 1.1;\n" + " namespace \"xx:y\";\n" + " prefix xx;\n" + " container c;\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* module with import statement */ + orig = + "module b26 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " import b26xx {\n" + " prefix xx;\n" + " }\n" + "\n" + " augment \"/xx:c\" {\n" + " container e;\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + + /* pyang -f tree --tree-path /c/e ... but prefixes modified */ + expect = + "module: b26\n" + " +--rw xx:c\n" + " +--rw e\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + node = lys_find_path(UTEST_LYCTX, NULL, "/b26xx:c/b26:e", 0); + CHECK_POINTER(node, 1); + assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static LY_ERR +local_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +print_parsed_submodule(void **state) +{ + TEST_LOCAL_SETUP; + + orig = "module a27 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " include \"a27-sub\";\n" + "}\n"; + + char *submodule = + "submodule a27-sub {\n" + " yang-version 1.1;\n" + " belongs-to a27 {\n" + " prefix x;\n" + " }\n" + "\n" + " container h {\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* edited pyang output */ + expect = + "submodule: a27-sub\n" + " +--rw h\n" + " +--rw b? string\n"; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, local_imp_clb, submodule); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + assert_int_equal(LY_SUCCESS, lys_print_submodule(UTEST_OUT, mod->parsed->includes[0].submodule, LYS_OUT_TREE, 72, 0)); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + TEST_LOCAL_TEARDOWN; +} + +static void +yang_data(void **state) +{ + TEST_LOCAL_SETUP; + + 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)); + + orig = + "module a28 {\n" + " yang-version 1.1;\n" + " namespace \"x:y\";\n" + " prefix x;\n" + "\n" + " import ietf-restconf {\n" + " prefix rc;\n" + " revision-date 2017-01-26;\n" + " }\n" + "\n" + " rc:yang-data \"tmp1\" {\n" + " container cont1 {\n" + " leaf lf {\n" + " type string;\n" + " }\n" + " list l2 {\n" + " key\n" + " \"a b\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf b {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " rc:yang-data \"tmp2\" {\n" + " container con2 {\n" + " leaf lf {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " rc:yang-data \"tmp3\" {\n" + " uses g1;\n" + " uses g2;\n" + " }\n" + " rc:yang-data \"tmp4\" {\n" + " choice x {\n" + " case a {\n" + " container z;\n" + " }\n" + " case b {\n" + " container y;\n" + " }\n" + " }\n" + " }\n" + "\n" + " grouping g1 {\n" + " description\n" + " \"Some Text\";\n" + " }\n" + "\n" + " grouping g2 {\n" + " container cont3;\n" + " }\n" + " container mont;\n" + "}\n"; + + /* from pyang (--tree-print-yang-data --tree-print-groupings -p "...") + * but with these adjustments: + * - <flags> is always '--' for yang-data nodes + * - yang-data tmp3 has two 'uses' nodes + * - grouping g2 has ':' character at the end + */ + expect = + "module: a28\n" + " +--rw mont\n" + "\n" + " grouping g1\n" + " grouping g2:\n" + " +---- cont3\n" + "\n" + " yang-data tmp1:\n" + " +---- cont1\n" + " +---- lf? string\n" + " +---- l2* [a b]\n" + " +---- a string\n" + " +---- b string\n" + " yang-data tmp2:\n" + " +---- con2\n" + " +---- lf? string\n" + " yang-data tmp3:\n" + " +---u g1\n" + " +---u g2\n" + " yang-data tmp4:\n" + " +---- (x)?\n" + " +--:(a)\n" + " | +---- z\n" + " +--:(b)\n" + " +---- y\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + + ly_out_reset(UTEST_OUT); + + /* from pyang (--tree-print-yang-data -p "...") + * but with these adjustments: + * <flags> is always '--' for yang-data nodes + */ + expect = + "module: a28\n" + " +--rw mont\n" + "\n" + " yang-data tmp1:\n" + " +---- cont1\n" + " +---- lf? string\n" + " +---- l2* [a b]\n" + " +---- a string\n" + " +---- b string\n" + " yang-data tmp2:\n" + " +---- con2\n" + " +---- lf? string\n" + " yang-data tmp3:\n" + " +---- cont3\n" + " yang-data tmp4:\n" + " +---- (x)?\n" + " +--:(a)\n" + " | +---- z\n" + " +--:(b)\n" + " +---- y\n"; + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static LY_ERR +getter(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free) +{ + struct ly_ctx *ctx; + struct lyd_node *data = NULL; + + ctx = ext->module->ctx; + if (user_data) { + assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, user_data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &data)); + } + + *ext_data = data; + *ext_data_free = 1; + return LY_SUCCESS; +} + +#define SM_MODNAME_EXT "sm-extension" +#define SM_MOD_EXT_NAMESPACE "urn:sm-ext" +#define SM_PREF "sm" +#define SCHEMA_REF_INLINE "<inline></inline>" +#define SCHEMA_REF_SHARED(REF) "<shared-schema>"REF"</shared-schema>" + +#define EXT_DATA(MPMOD_NAME, MODULES, SCHEMA_REF) \ + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"\n" \ + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">\n" \ + "<module-set>\n" \ + " <name>test-set</name>\n" \ + " <module>\n" \ + " <name>"SM_MODNAME_EXT"</name>\n" \ + " <namespace>"SM_MOD_EXT_NAMESPACE"</namespace>\n" \ + " </module>\n" \ + MODULES \ + "</module-set>\n" \ + "<content-id>1</content-id>\n" \ + "</yang-library>\n" \ + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">\n" \ + "<module-set-id>1</module-set-id>\n" \ + "</modules-state>\n" \ + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">\n" \ + "<namespace>\n" \ + " <prefix>"SM_PREF"</prefix>\n" \ + " <uri>x:"MPMOD_NAME"</uri>\n" \ + "</namespace>\n" \ + "<mount-point>\n" \ + " <module>"MPMOD_NAME"</module>\n" \ + " <label>mnt-root</label>\n" \ + SCHEMA_REF \ + "</mount-point>\n" \ + "</schema-mounts>" + +#define SM_MOD_MAIN(NAME, BODY) \ + "module "NAME" {\n" \ + " yang-version 1.1;\n" \ + " namespace \"x:"NAME"\";\n" \ + " prefix \"x\";\n" \ + " import ietf-yang-schema-mount {\n" \ + " prefix yangmnt;\n" \ + " }\n" \ + BODY \ + "}" + +static void +mount_point(void **state) +{ + char *data; + + TEST_LOCAL_SETUP; + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + /* interested in sm-extension.yang and sm-mod.yang */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + + /* + * 'mp' flag for list and container + */ + orig = SM_MOD_MAIN("a29", + "list lt {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n" + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a29\n" + " +--mp lt* [name]\n" + " | +--rw name string\n" + " +--mp cont\n"; + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mount schema by 'inline' schema-ref + */ + orig = SM_MOD_MAIN("a30", + "list lt {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a30\n" + " +--mp lt* [name]\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw name string\n"; + data = EXT_DATA("a30", "", SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mount schema into empty container + */ + orig = SM_MOD_MAIN("a31", + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n" + "leaf lf {\n" + " type string;\n" + "}\n"); + expect = + "module: a31\n" + " +--mp cont\n" + " | +--rw tlist/* [name]\n" + " | | +--rw name uint32\n" + " | +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw lf? string\n"; + data = EXT_DATA("a31", "", SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mount schema into non-empty container + */ + orig = SM_MOD_MAIN("a32", + "container cont {\n" + " leaf lf1 {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"mnt-root\";\n" + " leaf lf2 {\n" + " type string;\n" + " }\n" + "}\n"); + expect = + "module: a32\n" + " +--mp cont\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw lf1? string\n" + " +--rw lf2? string\n"; + data = EXT_DATA("a32", "", SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mounting with parent-reference + */ + orig = SM_MOD_MAIN("a33", + "list pr {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " leaf prlf {\n" + " type string;\n" + " }\n" + "}\n" + "leaf lf {\n" + " type string;\n" + "}\n" + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + " list lt {\n" + " key \"name\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"); + expect = + "module: a33\n" + " +--rw pr* [name]\n" + " | +--rw name string\n" + " | +--rw prlf? string\n" + " +--rw lf? string\n" + " +--mp cont\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw pr@* [name]\n" + " | +--rw prlf? string\n" + " +--rw lf@? string\n" + " +--rw lt* [name]\n" + " +--rw name string\n"; + data = EXT_DATA("a33", "", SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":prlf</parent-reference>\n" + "<parent-reference>/"SM_PREF ":lf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mounting with parent-reference into empty container + */ + orig = SM_MOD_MAIN("a34", + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n" + "leaf lf {\n" + " type string;\n" + "}\n"); + expect = + "module: a34\n" + " +--mp cont\n" + " | +--rw tlist/* [name]\n" + " | | +--rw name uint32\n" + " | +--rw tcont/\n" + " | | +--rw tleaf? uint32\n" + " | +--rw lf@? string\n" + " +--rw lf? string\n"; + data = EXT_DATA("a34", "", + SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":lf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * mounting module which is only parsed + */ + orig = SM_MOD_MAIN("a35", + "import sm-mod {\n" + " prefix smm;\n" + "}\n" + "container pr {\n" + " leaf prlf {\n" + " type uint32;\n" + " }\n" + "}\n" + "list lt {\n" + " key \"name\";\n" + " yangmnt:mount-point \"mnt-root\";\n" + " leaf name {\n" + " type string;\n" + " }\n" + "}\n"); + expect = + "module: a35\n" + " +--rw pr\n" + " | +--rw prlf? uint32\n" + " +--mp lt* [name]\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--mp ncmp/\n" + " +--rw not-compiled/\n" + " | +--rw first? string\n" + " | +--rw second? string\n" + " +--rw pr@\n" + " | +--rw prlf? uint32\n" + " +--rw name string\n"; + data = EXT_DATA("a35", + "<module>\n" + " <name>sm-mod</name>\n" + " <namespace>urn:sm-mod</namespace>\n" + "</module>\n", + SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":prlf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * notifications and rpcs in mounted module + */ + orig = SM_MOD_MAIN("a36", + "container cont {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a36\n" + " +--mp cont\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw cont/\n" + " | +---x cr\n" + " | | +---w input\n" + " | | | +---w in? string\n" + " | | +--ro output\n" + " | | +--ro out? string\n" + " | +---n cn\n" + " +---x r1/\n" + " +---x r2/\n" + " +---n n1/\n" + " +---n n2/\n"; + data = EXT_DATA("a36", + "<module>\n" + " <name>sm-rpcnotif</name>\n" + " <namespace>urn:rpcnotif</namespace>\n" + "</module>\n", + SCHEMA_REF_INLINE); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* + * parent-ref composes the '@' subtree + */ + orig = SM_MOD_MAIN("a37", + "container pr {\n" + " leaf ignored_node {\n" + " type string;\n" + " }\n" + " container cont {\n" + " leaf ignored_lf {\n" + " type uint32;\n" + " }\n" + " }\n" + " container ignored_subtree {\n" + " leaf ignored_lf {\n" + " type uint32;\n" + " }\n" + " }\n" + " container cont_sibl {\n" + " leaf slf {\n" + " type string;\n" + " }\n" + " }\n" + " leaf lf {\n" + " type uint32;\n" + " }\n" + "}\n" + "container cont_mount {\n" + " yangmnt:mount-point \"mnt-root\";\n" + "}\n"); + expect = + "module: a37\n" + " +--rw pr\n" + " | +--rw ignored_node? string\n" + " | +--rw cont\n" + " | | +--rw ignored_lf? uint32\n" + " | +--rw ignored_subtree\n" + " | | +--rw ignored_lf? uint32\n" + " | +--rw cont_sibl\n" + " | | +--rw slf? string\n" + " | +--rw lf? uint32\n" + " +--mp cont_mount\n" + " +--rw tlist/* [name]\n" + " | +--rw name uint32\n" + " +--rw tcont/\n" + " | +--rw tleaf? uint32\n" + " +--rw pr@\n" + " +--rw cont\n" + " +--rw cont_sibl\n" + " | +--rw slf? string\n" + " +--rw lf? uint32\n"; + data = EXT_DATA("a37", "", SCHEMA_REF_SHARED( + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont_sibl/slf</parent-reference>\n" + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont</parent-reference>\n" + "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":lf</parent-reference>\n")); + ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data); + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_TEARDOWN; +} + +static void +structure(void **state) +{ + TEST_LOCAL_SETUP; + + orig = + "module example-module {\n" + " yang-version 1.1;\n" + " namespace \"urn:example:example-module\";\n" + " prefix exm;\n" + "\n" + " import ietf-yang-structure-ext {\n" + " prefix sx;\n" + " }\n" + "\n" + " sx:structure address-book {\n" + " list address {\n" + " key \"last first\";\n" + " leaf last {\n" + " type string;\n" + " }\n" + " leaf first {\n" + " type string;\n" + " }\n" + " leaf street {\n" + " type string;\n" + " }\n" + " leaf city {\n" + " type string;\n" + " }\n" + " leaf state {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* from RFC 8791, Appendix A.1 */ + expect = + "module: example-module\n" + "\n" + " structure address-book:\n" + " +-- address* [last first]\n" + " +-- last string\n" + " +-- first string\n" + " +-- street? string\n" + " +-- city? string\n" + " +-- state? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + orig = + "module example-module-aug {\n" + " yang-version 1.1;\n" + " namespace \"urn:example:example-module-aug\";\n" + " prefix exma;\n" + "\n" + " import ietf-yang-structure-ext {\n" + " prefix sx;\n" + " }\n" + " import example-module {\n" + " prefix exm;\n" + " }\n" + "\n" + " sx:augment-structure \"/exm:address-book/exm:address\" {\n" + " leaf county {\n" + " type string;\n" + " }\n" + " leaf zipcode {\n" + " type string;\n" + " }\n" + " }\n" + "}\n"; + + /* from RFC 8791, Appendix A.2 */ + expect = + "module: example-module-aug\n" + "\n" + " augment-structure /exm:address-book/exm:address:\n" + " +-- county? string\n" + " +-- zipcode? string\n"; + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + /* using lysc tree */ + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED); + + TEST_LOCAL_TEARDOWN; +} + +static void +annotation(void **state) +{ + TEST_LOCAL_SETUP; + + orig = + "module ann {\n" + " yang-version 1.1;\n" + " namespace \"urn:example:ann\";\n" + " prefix an;\n" + "\n" + " import ietf-yang-metadata {\n" + " prefix md;\n" + " }\n" + "\n" + " leaf lf1 {\n" + " type string;\n" + " }\n" + " md:annotation avalue {\n" + " type string;\n" + " }\n" + "}\n"; + + expect = + "module: ann\n" + " +--rw lf1? string\n"; + + /* annotation is ignored without error message */ + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + TEST_LOCAL_PRINT(mod, 72); + assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT)); + assert_string_equal(printed, expect); + ly_out_reset(UTEST_OUT); + + TEST_LOCAL_TEARDOWN; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(base_sections), + UTEST(node_status), + UTEST(node_config_flags), + UTEST(node_rpcs_flags), + UTEST(node_grouping_flags), + UTEST(notif_inside_container), + UTEST(node_choice), + UTEST(node_case), + UTEST(optional_opts), + UTEST(presence_container), + UTEST(node_keys), + UTEST(node_type_target), + UTEST(node_type_leafref), + UTEST(node_iffeatures), + UTEST(indent_wrapper), + UTEST(line_length_twiddling), + UTEST(break_before_leafref), + UTEST(break_before_leafref_and_iffeature), + UTEST(basic_unified_indent_before_type), + UTEST(twiddling_unified_indent_before_type), + UTEST(inheritance_of_config_flag), + UTEST(inheritance_of_status_flag), + UTEST(key_leaf_is_always_mandatory_true), + UTEST(transition_between_rpc_and_notif), + UTEST(local_augment), + UTEST(print_compiled_node), + UTEST(print_compiled_node_augment), + UTEST(print_parsed_submodule), + UTEST(yang_data), + UTEST(mount_point), + UTEST(structure), + UTEST(annotation), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c new file mode 100644 index 0000000..953aad8 --- /dev/null +++ b/tests/utests/schema/test_schema.c @@ -0,0 +1,1915 @@ +/** + * @file test_schema.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for schema related functions + * + * Copyright (c) 2018 - 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 <string.h> + +#include "compat.h" +#include "context.h" +#include "log.h" +#include "parser_schema.h" +#include "plugins_exts.h" +#include "set.h" +#include "tree_edit.h" +#include "tree_schema.h" +#include "tree_schema_internal.h" + +static LY_ERR +test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + if ((*module_data)[0] == '<') { + *format = LYS_IN_YIN; + } else { + *format = LYS_IN_YANG; + } + *free_module_data = NULL; + return LY_SUCCESS; +} + +#define TEST_YANG_MODULE_10(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "module "MOD_NAME" { namespace "MOD_NS"; prefix "MOD_PREFIX"; "CONTENT"}" + +#define TEST_YANG_MODULE_11(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "module "MOD_NAME" {yang-version 1.1; namespace "MOD_NS"; prefix "MOD_PREFIX"; "CONTENT"}" + +#define TEST_YIN_MODULE_10(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\""MOD_NAME"\">" \ + "<namespace uri=\""MOD_NS"\"/><prefix value=\""MOD_PREFIX"\"/>"CONTENT"</module>" + +#define TEST_YIN_MODULE_11(MOD_NAME, MOD_PREFIX, MOD_NS, CONTENT) \ + "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\""MOD_NAME"\"><yang-version value=\"1.1\"/>" \ + "<namespace uri=\""MOD_NS"\"/><prefix value=\""MOD_PREFIX"\"/>"CONTENT"</module>" + +#define TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, STR) \ + if (YIN) { \ + if (RFC7950) { \ + STR = TEST_YIN_MODULE_11(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } else { \ + STR = TEST_YIN_MODULE_10(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } \ + } else { /* YANG */ \ + if (RFC7950) { \ + STR = TEST_YANG_MODULE_11(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } else { \ + STR = TEST_YANG_MODULE_10(MOD_NAME, MOD_NAME, "urn:libyang:test:"MOD_NAME, CONTENT); \ + } \ + } + +#define TEST_SCHEMA_OK(RFC7950, YIN, MOD_NAME, CONTENT, RESULT) \ + { \ + const char *test_str__; \ + TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, &(RESULT))); \ + } + +#define TEST_SCHEMA_ERR(RFC7950, YIN, MOD_NAME, CONTENT, ERRMSG, ERRPATH) \ + { \ + const char *test_str__; \ + TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \ + assert_int_not_equal(lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, NULL), LY_SUCCESS); \ + CHECK_LOG_CTX(ERRMSG, ERRPATH, 0); \ + } + +#define TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, MOD_NAME, CONTENT, ERRMSG, ERRPATH, ERRLINE) \ + { \ + const char *test_str__; \ + TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \ + assert_int_not_equal(lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, NULL), LY_SUCCESS); \ + CHECK_LOG_CTX("Parsing module \""MOD_NAME"\" failed.", NULL, 0); \ + CHECK_LOG_CTX(ERRMSG, ERRPATH, ERRLINE); \ + } + +#define TEST_STMT_DUP(RFC7950, YIN, STMT, MEMBER, VALUE1, VALUE2, LINE) \ + if (YIN) { \ + TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, "dup", "", "Duplicate keyword \""MEMBER"\".", NULL, LINE); \ + } else { \ + TEST_SCHEMA_PARSE_ERR(RFC7950, YIN, "dup", STMT"{"MEMBER" "VALUE1";"MEMBER" "VALUE2";}", \ + "Duplicate keyword \""MEMBER"\".", NULL, LINE); \ + } + +#define TEST_STMT_SUBSTM_ERR(RFC7950, STMT, SUBSTMT, VALUE) ;\ + TEST_SCHEMA_PARSE_ERR(RFC7950, 0, "inv", STMT" test {"SUBSTMT" "VALUE";}", \ + "Invalid keyword \""SUBSTMT"\" as a child of \""STMT"\".", NULL, 1); + +struct module_clb_list { + const char *name; + const char *data; +}; + +static LY_ERR +module_clb(const char *mod_name, const char *UNUSED(mod_rev), const char *submod_name, + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + struct module_clb_list *list = (struct module_clb_list *)user_data; + + for (unsigned int i = 0; list[i].data; i++) { + + if ((submod_name && !strcmp(list[i].name, submod_name)) || + (!submod_name && mod_name && !strcmp(list[i].name, mod_name))) { + *module_data = list[i].data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; + } + } + return LY_EINVAL; +} + +static void +test_getnext(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node = NULL, *four; + const struct lysc_node_container *cont; + const struct lysc_action *rpc; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;" + "container a { container one {presence test;} leaf two {type string;} leaf-list three {type string;}" + " list four {config false;} choice x { leaf five {type string;} case y {leaf six {type string;}}}" + " anyxml seven; action eight {input {leaf eight-input {type string;}} output {leaf eight-output {type string;}}}" + " notification nine {leaf nine-data {type string;}}}" + "leaf b {type string;} leaf-list c {type string;} list d {config false;}" + "choice x { case empty-x { choice empty-xc { case nothing;}} leaf e {type string;} case y {leaf f {type string;}}} anyxml g;" + "rpc h {input {leaf h-input {type string;}} output {leaf h-output {type string;}}}" + "rpc i;" + "notification j {leaf i-data {type string;}}" + "notification k;}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("a", node->name); + cont = (const struct lysc_node_container *)node; + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("b", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("c", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("d", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("e", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("f", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("g", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("h", node->name); + rpc = (const struct lysc_action *)node; + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("i", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("j", node->name); + assert_non_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + assert_string_equal("k", node->name); + assert_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + /* Inside container */ + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("one", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("two", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("three", node->name); + assert_non_null(node = four = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("four", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("five", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("six", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("seven", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("eight", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + assert_string_equal("nine", node->name); + assert_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, 0)); + /* Inside RPC */ + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, 0)); + assert_string_equal("h-input", node->name); + assert_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, 0)); + + /* options */ + assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCHOICE)); + assert_string_equal("x", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCHOICE)); + assert_string_equal("seven", node->name); + + assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_NOCHOICE)); + assert_string_equal("seven", node->name); + + assert_non_null(node = lys_getnext(four, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE)); + assert_string_equal("five", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE)); + assert_string_equal("y", node->name); + assert_non_null(node = lys_getnext(node, (const struct lysc_node *)cont, mod->compiled, LYS_GETNEXT_WITHCASE)); + assert_string_equal("seven", node->name); + + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_INTONPCONT)); + assert_string_equal("one", node->name); + + assert_non_null(node = lys_getnext(NULL, (const struct lysc_node *)rpc, mod->compiled, LYS_GETNEXT_OUTPUT)); + assert_string_equal("h-output", node->name); + assert_null(node = lys_getnext(node, (const struct lysc_node *)rpc, mod->compiled, LYS_GETNEXT_OUTPUT)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; rpc c;}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0)); + assert_string_equal("c", node->name); + assert_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; notification d;}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0)); + assert_string_equal("d", node->name); + assert_null(node = lys_getnext(node, NULL, mod->compiled, 0)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e; container c {container cc;} leaf a {type string;}}", LYS_IN_YANG, &mod)); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0)); + assert_string_equal("c", node->name); + assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_INTONPCONT)); + assert_string_equal("a", node->name); +} + +static void +test_date(void **UNUSED(state)) +{ + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, NULL, 0, "date")); + CHECK_LOG_LASTMSG("Invalid argument date (lysp_check_date())."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "x", 1, "date")); + CHECK_LOG_LASTMSG("Invalid length 1 of a date."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "nonsencexx", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"nonsencexx\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "123x-11-11", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"123x-11-11\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-13-11", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-13-11\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-11-41", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-11-41\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-02-29", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-02-29\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018.02-28", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018.02-28\" of \"date\"."); + assert_int_equal(LY_EINVAL, lysp_check_date(NULL, "2018-02.28", 10, "date")); + CHECK_LOG_LASTMSG("Invalid value \"2018-02.28\" of \"date\"."); + + assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-11-11", 10, "date")); + assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-02-28", 10, "date")); + assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2016-02-29", 10, "date")); +} + +static void +test_revisions(void **UNUSED(state)) +{ + struct lysp_revision *revs = NULL, *rev; + + /* revisions are stored in wrong order - the newest is the last */ + LY_ARRAY_NEW_RET(NULL, revs, rev, ); + strcpy(rev->date, "2018-01-01"); + LY_ARRAY_NEW_RET(NULL, revs, rev, ); + strcpy(rev->date, "2018-12-31"); + + assert_int_equal(2, LY_ARRAY_COUNT(revs)); + assert_string_equal("2018-01-01", revs[0].date); + assert_string_equal("2018-12-31", revs[1].date); + /* the order should be fixed, so the newest revision will be the first in the array */ + lysp_sort_revisions(revs); + assert_string_equal("2018-12-31", revs[0].date); + assert_string_equal("2018-01-01", revs[1].date); + + LY_ARRAY_FREE(revs); +} + +static void +test_collision_typedef(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision with a built-in type */ + str = "module a {namespace urn:a; prefix a; typedef binary {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"binary\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef bits {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"bits\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef boolean {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"boolean\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef decimal64 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"decimal64\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef empty {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"empty\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef enumeration {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"enumeration\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int8 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int8\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int16 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int16\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int32 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int32\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef int64 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"int64\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef instance-identifier {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"instance-identifier\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef identityref {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"identityref\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef leafref {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"leafref\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef string {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"string\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef union {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"union\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint8 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint8\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint16 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint16\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint32 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint32\" of typedef statement - name collision with a built-in type.", NULL, 0); + str = "module a {namespace urn:a; prefix a; typedef uint64 {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"uint64\" of typedef statement - name collision with a built-in type.", NULL, 0); + + str = "module mytypes {namespace urn:types; prefix t; typedef binary_ {type string;} typedef bits_ {type string;} typedef boolean_ {type string;} " + "typedef decimal64_ {type string;} typedef empty_ {type string;} typedef enumeration_ {type string;} typedef int8_ {type string;} typedef int16_ {type string;}" + "typedef int32_ {type string;} typedef int64_ {type string;} typedef instance-identifier_ {type string;} typedef identityref_ {type string;}" + "typedef leafref_ {type string;} typedef string_ {type int8;} typedef union_ {type string;} typedef uint8_ {type string;} typedef uint16_ {type string;}" + "typedef uint32_ {type string;} typedef uint64_ {type string;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + + /* collision in node's scope */ + str = "module a {namespace urn:a; prefix a; container c {typedef y {type int8;} typedef y {type string;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of typedef statement - name collision with sibling type.", NULL, 0); + + /* collision with parent node */ + str = "module a {namespace urn:a; prefix a; container c {container d {typedef y {type int8;}} typedef y {type string;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of typedef statement - name collision with another scoped type.", NULL, 0); + + /* collision with module's top-level */ + str = "module a {namespace urn:a; prefix a; typedef x {type string;} container c {typedef x {type int8;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of submodule's node with module's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {typedef x {type string;}}}"); + str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of module's node with submodule's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type int8;}}"); + str = "module a {namespace urn:a; prefix a; include b; container c {typedef x {type string;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of submodule's node with another submodule's top-level */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} typedef g {type int;}}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {typedef g {type int;}}}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - scoped type collide with a top-level type.", NULL, 0); + + /* collision of module's top-levels */ + str = "module a {namespace urn:a; prefix a; typedef test {type string;} typedef test {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"test\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} typedef g {type int;} typedef g {type int;}}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type string;}}"); + str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} typedef g {type int;}}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} typedef g {type int;}}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL, 0); + + /* error in type-stmt */ + str = "module a {namespace urn:a; prefix a; container c {typedef x {type t{}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected end-of-input.", NULL, 1); + + /* no collision if the same names are in different scope */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "container c {typedef g {type int;}} container d {typedef g {type int;}}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); +} + +static void +test_collision_grouping(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision in node's scope */ + str = "module a {namespace urn:a; prefix a; container c {grouping y; grouping y;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of grouping statement - name collision with sibling grouping.", NULL, 0); + + /* collision with parent node */ + str = "module a {namespace urn:a; prefix a; container c {container d {grouping y;} grouping y;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"y\" of grouping statement - name collision with another scoped grouping.", NULL, 0); + + /* collision with module's top-level */ + str = "module a {namespace urn:a; prefix a; grouping x; container c {grouping x;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of submodule's node with module's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {grouping x;}}"); + str = "module a {namespace urn:a; prefix a; include b; grouping x;}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of module's node with submodule's top-level */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}"); + str = "module a {namespace urn:a; prefix a; include b; container c {grouping x;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of submodule's node with another submodule's top-level */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} grouping g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {grouping g;}}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision of module's top-levels */ + str = "module a {namespace urn:a; prefix a; grouping test; grouping test;}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"test\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} grouping g; grouping g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}"); + str = "module a {namespace urn:a; prefix a; include b; grouping x;}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} grouping g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} grouping g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL, 0); + + /* collision in nested groupings, top-level */ + str = "module a {namespace urn:a; prefix a; grouping g {grouping g;}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL, 0); + + /* collision in nested groupings, in node */ + str = "module a {namespace urn:a; prefix a; container c {grouping g {grouping g;}}}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another scoped grouping.", NULL, 0); + + /* no collision if the same names are in different scope */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "container c {grouping g;} container d {grouping g;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Locally scoped grouping \"g\" not used.", NULL, 0); + CHECK_LOG_CTX("Locally scoped grouping \"g\" not used.", NULL, 0); +} + +static void +test_collision_identity(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision of module's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; identity g; identity g;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} identity g; identity g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} identity g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; identity g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} identity g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} identity g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL, 0); +} + +static void +test_collision_feature(void **state) +{ + const char *str; + char *submod; + struct module_clb_list list[3] = {0}; + + list[0].name = "asub"; + list[1].name = "bsub"; + + /* collision of module's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; feature g; feature g;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); + + /* collision of submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} feature g; feature g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); + + /* collision of module's top-level with submodule's top-levels */ + submod = "submodule asub {belongs-to a {prefix a;} feature g;}"; + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; feature g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); + + /* collision of submodule's top-level with another submodule's top-levels */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}"; + list[0].data = "submodule asub {belongs-to a {prefix a;} feature g;}"; + list[1].data = "submodule bsub {belongs-to a {prefix a;} feature g;}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL, 0); +} + +static void +test_accessible_tree(void **state) +{ + const char *str; + + /* config -> config */ + str = "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " container cont {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* config -> state leafref */ + str = "module b {\n" + " namespace urn:b;\n" + " prefix b;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " leaf l2 {\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid leafref path \"/cont/l\" - target is supposed to represent configuration data" + " (as the leafref does), but it does not.", "/b:cont2/l2", 0); + + /* config -> state must */ + str = "module b {\n" + " namespace urn:b;\n" + " prefix b;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"cont\" for parent \"<config-root>\" not found; in expr \"../../cont\" " + "with context node \"/b:cont2/l2\".", NULL, 0); + + /* state -> config */ + str = "module c {\n" + " namespace urn:c;\n" + " prefix c;\n" + " container cont {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " container cont2 {\n" + " config false;\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* notif -> state */ + str = "module d {\n" + " namespace urn:d;\n" + " prefix d;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " notification notif {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* notif -> notif */ + str = "module e {\n" + " namespace urn:e;\n" + " prefix e;\n" + " notification notif {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " leaf l2 {\n" + " must ../../notif/l;\n" + " type leafref {\n" + " path /notif/l;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* rpc input -> state */ + str = "module f {\n" + " namespace urn:f;\n" + " prefix f;\n" + " container cont {\n" + " config false;\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " must ../../cont/l;\n" + " type leafref {\n" + " path /cont/l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* rpc input -> rpc input */ + str = "module g {\n" + " namespace urn:g;\n" + " prefix g;\n" + " rpc rp {\n" + " input {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " leaf l2 {\n" + " must ../l;\n" + " type leafref {\n" + " path /rp/l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* rpc input -> rpc output leafref */ + str = "module h {\n" + " namespace urn:h;\n" + " prefix h;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " type leafref {\n" + " path /rp/l;\n" + " }\n" + " }\n" + " }\n" + " output {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Not found node \"l\" in path.", "/h:rp/input/l2", 0); + + /* rpc input -> rpc output must */ + str = "module h {\n" + " namespace urn:h;\n" + " prefix h;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " must ../l;\n" + " type empty;\n" + " }\n" + " }\n" + " output {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"l\" for parent \"/h:rp\" not found; in expr \"../l\" with context node \"/h:rp/input/l2\".", + NULL, 0); + + /* rpc input -> notif leafref */ + str = "module i {\n" + " namespace urn:i;\n" + " prefix i;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " type leafref {\n" + " path ../../notif/l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " notification notif {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Not found node \"notif\" in path.", "/i:rp/input/l2", 0); + + /* rpc input -> notif must */ + str = "module i {\n" + " namespace urn:i;\n" + " prefix i;\n" + " rpc rp {\n" + " input {\n" + " leaf l2 {\n" + " must /notif/l;\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + " notification notif {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"notif\" for parent \"<root>\" not found; in expr \"/notif\" " + "with context node \"/i:rp/input/l2\".", NULL, 0); + + /* action output -> state */ + str = "module j {\n" + " yang-version 1.1;\n" + " namespace urn:j;\n" + " prefix j;\n" + " container cont {\n" + " list ll {\n" + " key k;\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " output {\n" + " leaf l2 {\n" + " must /cont/l;\n" + " type leafref {\n" + " path ../../../l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " leaf l {\n" + " config false;\n" + " type empty;\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* action output -> action input leafref */ + str = "module k {\n" + " yang-version 1.1;\n" + " namespace urn:k;\n" + " prefix k;\n" + " container cont {\n" + " list ll {\n" + " key k;\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " input {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " output {\n" + " leaf l2 {\n" + " type leafref {\n" + " path ../l;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Not found node \"l\" in path.", "/k:cont/ll/act/output/l2", 0); + + /* action output -> action input must */ + str = "module k {\n" + " yang-version 1.1;\n" + " namespace urn:k;\n" + " prefix k;\n" + " container cont {\n" + " list ll {\n" + " key k;\n" + " leaf k {\n" + " type string;\n" + " }\n" + " action act {\n" + " input {\n" + " leaf l {\n" + " type empty;\n" + " }\n" + " }\n" + " output {\n" + " leaf l2 {\n" + " must /cont/ll/act/l;\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Schema node \"l\" for parent \"/k:cont/ll/act\" not found; in expr \"/cont/ll/act/l\" " + "with context node \"/k:cont/ll/act/output/l2\".", NULL, 0); +} + +static void +test_includes(void **state) +{ + struct lys_module *mod; + + { + /* YANG 1.0 - the missing include sub_a_two in main_a will be injected from sub_a_one */ + struct module_clb_list list[] = { + {"main_a", "module main_a { namespace urn:test:main_a; prefix ma; include sub_a_one;}"}, + {"sub_a_one", "submodule sub_a_one { belongs-to main_a { prefix ma; } include sub_a_two;}"}, + {"sub_a_two", "submodule sub_a_two { belongs-to main_a { prefix ma; } }"}, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + mod = ly_ctx_load_module(UTEST_LYCTX, "main_a", NULL, NULL); + assert_non_null(mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->includes)); + assert_true(mod->parsed->includes[1].injected); + } + + { + /* YANG 1.1 - the missing include sub_b_two in main_b is error */ + struct module_clb_list list[] = { + {"main_b", "module main_b { yang-version 1.1; namespace urn:test:main_b; prefix mb; include sub_b_one;}"}, + {"sub_b_one", "submodule sub_b_one { yang-version 1.1; belongs-to main_b { prefix mb; } include sub_b_two;}"}, + {"sub_b_two", "submodule sub_b_two { yang-version 1.1; belongs-to main_b { prefix mb; } }"}, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + mod = ly_ctx_load_module(UTEST_LYCTX, "main_b", NULL, NULL); + assert_null(mod); + CHECK_LOG_CTX("Loading \"main_b\" module failed.", NULL, 0); + CHECK_LOG_CTX("Data model \"main_b\" not found in local searchdirs.", NULL, 0); + CHECK_LOG_CTX("Parsing module \"main_b\" failed.", NULL, 0); + CHECK_LOG_CTX("Including \"sub_b_one\" submodule into \"main_b\" failed.", NULL, 0); + CHECK_LOG_CTX("Data model \"sub_b_one\" not found in local searchdirs.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule \"sub_b_one\" failed.", NULL, 0); + CHECK_LOG_CTX("YANG 1.1 requires all submodules to be included from main module. But submodule \"sub_b_one\" includes " + "submodule \"sub_b_two\" which is not included by main module \"main_b\".", NULL, 0); + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_b_one) are not necessary.", + NULL, 0); + } + + { + /* YANG 1.1 - all includes are in main_c, includes in submodules are not necessary, so expect warning */ + struct module_clb_list list[] = { + {"main_c", "module main_c { yang-version 1.1; namespace urn:test:main_c; prefix mc; include sub_c_one; include sub_c_two;}"}, + {"sub_c_one", "submodule sub_c_one { yang-version 1.1; belongs-to main_c { prefix mc; } include sub_c_two;}"}, + {"sub_c_two", "submodule sub_c_two { yang-version 1.1; belongs-to main_c { prefix mc; } include sub_c_one;}"}, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list); + mod = ly_ctx_load_module(UTEST_LYCTX, "main_c", NULL, NULL); + assert_non_null(mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->includes)); + assert_false(mod->parsed->includes[1].injected); + /* result is ok, but log includes the warning */ + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_c_two) are not necessary.", + NULL, 0); + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_c_one) are not necessary.", + NULL, 0); + } +} + +static void +test_key_order(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node; + + struct module_clb_list list1[] = { + { + "a", "module a {" + "yang-version 1.1;" + "namespace urn:test:a;" + "prefix a;" + "list l {" + " key \"k1 k2\";" + " leaf k2 {type string;}" + " leaf k1 {type string;}" + "}" + "}" + }, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list1); + mod = ly_ctx_load_module(UTEST_LYCTX, "a", NULL, NULL); + assert_non_null(mod); + + node = lysc_node_child(mod->compiled->data); + assert_string_equal("k1", node->name); + node = node->next; + assert_string_equal("k2", node->name); + + struct module_clb_list list2[] = { + { + "b", "module b {" + "yang-version 1.1;" + "namespace urn:test:b;" + "prefix b;" + "list l {" + " key \"k1 k2 k3 k4\";" + " leaf k4 {type string;}" + " container c {" + " leaf l1 {type string;}" + " }" + " leaf k2 {type string;}" + " leaf l2 {type string;}" + " leaf k1 {type string;}" + " leaf k3 {type string;}" + "}" + "}" + }, + {NULL, NULL} + }; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list2); + mod = ly_ctx_load_module(UTEST_LYCTX, "b", NULL, NULL); + assert_non_null(mod); + + node = lysc_node_child(mod->compiled->data); + assert_string_equal("k1", node->name); + node = node->next; + assert_string_equal("k2", node->name); + node = node->next; + assert_string_equal("k3", node->name); + node = node->next; + assert_string_equal("k4", node->name); +} + +static void +test_disabled_enum(void **state) +{ + const char *str; + + /* no enabled enum */ + str = "module a {" + "yang-version 1.1;" + "namespace urn:test:a;" + "prefix a;" + "feature f;" + "leaf l {type enumeration {" + " enum e1 {if-feature f;}" + " enum e2 {if-feature f;}" + "}}" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Node \"l\" without any (or all disabled) valid values.", "/a:l", 0); + + /* disabled default value */ + str = "module a {" + "yang-version 1.1;" + "namespace urn:test:a;" + "prefix a;" + "feature f;" + "leaf l {" + " type enumeration {" + " enum e1 {if-feature f;}" + " enum e2;" + " }" + " default e1;" + "}" + "}"; + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid enumeration value \"e1\".).", "/a:l", 0); +} + +static void +test_identity(void **state) +{ + struct lys_module *mod, *mod_imp; + + /* + * parsing YANG + */ + TEST_STMT_DUP(1, 0, "identity id", "description", "a", "b", 1); + TEST_STMT_DUP(1, 0, "identity id", "reference", "a", "b", 1); + TEST_STMT_DUP(1, 0, "identity id", "status", "current", "obsolete", 1); + + /* full content */ + TEST_SCHEMA_OK(1, 0, "identityone", + "identity test {base \"a\";base b; description text;reference \'another text\';status current; if-feature x;if-feature y; identityone:ext;}" + "identity a; identity b; extension ext; feature x; feature y;", mod); + assert_non_null(mod->parsed->identities); + assert_int_equal(3, LY_ARRAY_COUNT(mod->parsed->identities)); + + /* invalid substatement */ + TEST_STMT_SUBSTM_ERR(0, "identity", "organization", "XXX"); + + /* + * parsing YIN + */ + /* max subelems */ + TEST_SCHEMA_OK(1, 1, "identityone-yin", "<identity name=\"ident-name\">" + "<if-feature name=\"iff\"/>" + "<base name=\"base-name\"/>" + "<status value=\"deprecated\"/>" + "<description><text>desc</text></description>" + "<reference><text>ref</text></reference>" + "<myext:ext xmlns:myext=\"urn:libyang:test:identityone-yin\"/>" + "</identity><extension name=\"ext\"/><identity name=\"base-name\"/><feature name=\"iff\"/>", mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->identities)); + assert_string_equal(mod->parsed->identities[0].name, "ident-name"); + assert_string_equal(mod->parsed->identities[0].bases[0], "base-name"); + assert_string_equal(mod->parsed->identities[0].iffeatures[0].str, "iff"); + assert_string_equal(mod->parsed->identities[0].dsc, "desc"); + assert_string_equal(mod->parsed->identities[0].ref, "ref"); + assert_true(mod->parsed->identities[0].flags & LYS_STATUS_DEPRC); + assert_string_equal(mod->parsed->identities[0].exts[0].name, "myext:ext"); + + /* min subelems */ + TEST_SCHEMA_OK(1, 1, "identitytwo-yin", "<identity name=\"ident-name\" />", mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->parsed->identities)); + assert_string_equal(mod->parsed->identities[0].name, "ident-name"); + + /* invalid substatement */ + TEST_SCHEMA_PARSE_ERR(0, 1, "inv", "<identity name=\"ident-name\"><if-feature name=\"iff\"/></identity>", + "Invalid sub-elemnt \"if-feature\" of \"identity\" element - " + "this sub-element is allowed only in modules with version 1.1 or newer.", NULL, 1); + + /* + * compiling + */ + TEST_SCHEMA_OK(0, 0, "a", "identity a1;", mod_imp); + TEST_SCHEMA_OK(1, 0, "b", "import a {prefix a;}" + "identity b1; identity b2; identity b3 {base b1; base b:b2; base a:a1;}" + "identity b4 {base b:b1; base b3;}", mod); + assert_non_null(mod_imp->compiled); + assert_non_null(mod_imp->identities); + assert_non_null(mod->identities); + assert_non_null(mod_imp->identities[0].derived); + assert_int_equal(1, LY_ARRAY_COUNT(mod_imp->identities[0].derived)); + assert_ptr_equal(mod_imp->identities[0].derived[0], &mod->identities[2]); + assert_non_null(mod->identities[0].derived); + assert_int_equal(2, LY_ARRAY_COUNT(mod->identities[0].derived)); + assert_ptr_equal(mod->identities[0].derived[0], &mod->identities[2]); + assert_ptr_equal(mod->identities[0].derived[1], &mod->identities[3]); + assert_non_null(mod->identities[1].derived); + assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[1].derived)); + assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[2]); + assert_non_null(mod->identities[2].derived); + assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[2].derived)); + assert_ptr_equal(mod->identities[2].derived[0], &mod->identities[3]); + + TEST_SCHEMA_OK(1, 0, "c", "identity c2 {base c1;} identity c1;", mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[1].derived)); + assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[0]); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} identity i1;}"); + TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}", "Unable to find base (i2) of identity \"i1\".", "/inv:{identity='i1'}"); + TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i1;}", "Identity \"i1\" is derived from itself.", "/inv:{identity='i1'}"); + TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}identity i2 {base i3;}identity i3 {base i1;}", + "Identity \"i1\" is indirectly derived from itself.", "/inv:{identity='i3'}"); + + /* base in non-implemented module */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module base {namespace \"urn\"; prefix b; identity i1; identity i2 {base i1;}}"); + TEST_SCHEMA_OK(0, 0, "ident", "import base {prefix b;} identity ii {base b:i1;}", mod); + + /* default value from non-implemented module */ + TEST_SCHEMA_ERR(0, 0, "ident2", "import base {prefix b;} leaf l {type identityref {base b:i1;} default b:i2;}", + "Invalid default - value does not fit the type (Invalid identityref \"b:i2\" value" + " - identity found in non-implemented module \"base\".).", "/ident2:l"); + + /* default value in typedef from non-implemented module */ + TEST_SCHEMA_ERR(0, 0, "ident2", "import base {prefix b;} typedef t1 {type identityref {base b:i1;} default b:i2;}" + "leaf l {type t1;}", "Invalid default - value does not fit the type (Invalid" + " identityref \"b:i2\" value - identity found in non-implemented module \"base\".).", "/ident2:l"); + + /* + * printing + */ + + /* + * cleanup + */ +} + +static void +test_feature(void **state) +{ + struct lys_module *mod; + const struct lysp_feature *f; + + /* + * parsing YANG + */ + + TEST_STMT_DUP(1, 0, "feature f", "description", "a", "b", 1); + TEST_STMT_DUP(1, 0, "feature f", "reference", "a", "b", 1); + TEST_STMT_DUP(1, 0, "feature f", "status", "current", "obsolete", 1); + + /* full content */ + TEST_SCHEMA_OK(1, 0, "featureone", + "feature test {description text;reference \'another text\';status current; if-feature x; if-feature y; featureone:ext;}" + "extension ext; feature x; feature y;", mod); + assert_non_null(mod->parsed->features); + assert_int_equal(3, LY_ARRAY_COUNT(mod->parsed->features)); + + /* invalid substatement */ + TEST_STMT_SUBSTM_ERR(0, "feature", "organization", "XXX"); + + /* + * parsing YIN + */ + /* max subelems */ + TEST_SCHEMA_OK(0, 1, "featureone-yin", "<feature name=\"feature-name\">" + "<if-feature name=\"iff\"/>" + "<status value=\"deprecated\"/>" + "<description><text>desc</text></description>" + "<reference><text>ref</text></reference>" + "<myext:ext xmlns:myext=\"urn:libyang:test:featureone-yin\"/>" + "</feature><extension name=\"ext\"/><feature name=\"iff\"/>", mod); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->features)); + assert_string_equal(mod->parsed->features[0].name, "feature-name"); + assert_string_equal(mod->parsed->features[0].dsc, "desc"); + assert_true(mod->parsed->features[0].flags & LYS_STATUS_DEPRC); + assert_string_equal(mod->parsed->features[0].iffeatures[0].str, "iff"); + assert_string_equal(mod->parsed->features[0].ref, "ref"); + assert_string_equal(mod->parsed->features[0].exts[0].name, "myext:ext"); + + /* min subelems */ + TEST_SCHEMA_OK(0, 1, "featuretwo-yin", "<feature name=\"feature-name\"/>", mod) + assert_int_equal(1, LY_ARRAY_COUNT(mod->parsed->features)); + assert_string_equal(mod->parsed->features[0].name, "feature-name"); + + /* invalid substatement */ + TEST_SCHEMA_PARSE_ERR(0, 1, "inv", "<feature name=\"feature-name\"><organization><text>org</text></organization></feature>", + "Unexpected sub-element \"organization\" of \"feature\" element.", NULL, 1); + + /* + * compiling + */ + + TEST_SCHEMA_OK(1, 0, "a", "feature f1 {description test1;reference test2;status current;} feature f2; feature f3;\n" + "feature orfeature {if-feature \"f1 or f2\";}\n" + "feature andfeature {if-feature \"f1 and f2\";}\n" + "feature f6 {if-feature \"not f1\";}\n" + "feature f7 {if-feature \"(f2 and f3) or (not f1)\";}\n" + "feature f8 {if-feature \"f1 or f2 or f3 or orfeature or andfeature\";}\n" + "feature f9 {if-feature \"not not f1\";}", mod); + assert_non_null(mod->parsed->features); + assert_int_equal(9, LY_ARRAY_COUNT(mod->parsed->features)); + + /* all features are disabled by default */ + LY_ARRAY_FOR(mod->parsed->features, struct lysp_feature, f) { + assert_false(f->flags & LYS_FENABLED); + } + + /* some invalid expressions */ + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f{if-feature f1;}", + "Invalid value \"f1\" of if-feature - unable to find feature \"f1\".", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature 'f and';}", + "Invalid value \"f and\" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f{if-feature 'or';}", + "Invalid value \"or\" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature '(f1';}", + "Invalid value \"(f1\" of if-feature - non-matching opening and closing parentheses.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature 'f1)';}", + "Invalid value \"f1)\" of if-feature - non-matching opening and closing parentheses.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2{if-feature ---;}", + "Invalid value \"---\" of if-feature - unable to find feature \"---\".", NULL, 0); + TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1; feature f2{if-feature 'not f1';}", + "Invalid value \"not f1\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", NULL, 0); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} feature f1;}"); + TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1 {if-feature f2;} feature f2 {if-feature f1;}", + "Feature \"f1\" is indirectly referenced from itself.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(0, 0, "inv", "feature f1 {if-feature f1;}", + "Feature \"f1\" is referenced from itself.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f {if-feature ();}", + "Invalid value \"()\" of if-feature - number of features in expression does not match the required number of operands for the operations.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1(';}", + "Invalid value \"f1(\" of if-feature - non-matching opening and closing parentheses.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'and f1';}", + "Invalid value \"and f1\" of if-feature - missing feature/expression before \"and\" operation.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1 not ';}", + "Invalid value \"f1 not \" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f {if-feature 'f1 not not ';}", + "Invalid value \"f1 not not \" of if-feature - unexpected end of expression.", NULL, 0); + TEST_SCHEMA_PARSE_ERR(1, 0, "inv", "feature f1; feature f2; feature f {if-feature 'or f1 f2';}", + "Invalid value \"or f1 f2\" of if-feature - missing feature/expression before \"or\" operation.", NULL, 0); + + /* + * printing + */ + + /* + * cleanup + */ +} + +static void +test_extension_argument(void **state) +{ + struct lys_module *mod; + const char *mod_def_yang = "module a {\n" + " namespace \"urn:a\";\n" + " prefix a;\n\n" + " extension e {\n" + " argument name;\n" + " }\n\n" + " a:e \"aaa\";\n" + "}\n"; + const char *mod_def_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a\"/>\n" + " <extension name=\"e\">\n" + " <argument name=\"name\"/>\n" + " </extension>\n" + " <a:e name=\"aaa\"/>\n" + "</module>\n"; + const char *mod_test_yin, *mod_test_yang; + char *printed; + + mod_test_yang = "module b {\n" + " namespace \"urn:b\";\n" + " prefix b;\n\n" + " import a {\n" + " prefix a;\n" + " }\n\n" + " a:e \"xxx\";\n" + "}\n"; + mod_test_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"b\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:b=\"urn:b\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:b\"/>\n" + " <prefix value=\"b\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n" + " <a:e name=\"xxx\"/>\n" + "</module>\n"; + + /* from YANG */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); + + /* context reset */ + ly_ctx_destroy(UTEST_LYCTX); + ly_ctx_new(NULL, 0, &UTEST_LYCTX); + + /* from YIN */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); +} + +static void +test_extension_argument_element(void **state) +{ + struct lys_module *mod; + const char *mod_def_yang = "module a {\n" + " namespace \"urn:a\";\n" + " prefix a;\n\n" + " extension e {\n" + " argument name {\n" + " yin-element true;\n" + " }\n" + " }\n\n" + " a:e \"aaa\";\n" + "}\n"; + const char *mod_def_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a\"/>\n" + " <extension name=\"e\">\n" + " <argument name=\"name\">\n" + " <yin-element value=\"true\"/>\n" + " </argument>\n" + " </extension>\n" + " <a:e>\n" + " <a:name>aaa</a:name>\n" + " </a:e>\n" + "</module>\n"; + const char *mod_test_yin, *mod_test_yang; + char *printed; + + mod_test_yang = "module b {\n" + " namespace \"urn:b\";\n" + " prefix b;\n\n" + " import a {\n" + " prefix a;\n" + " }\n\n" + " a:e \"xxx\";\n" + "}\n"; + mod_test_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"b\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:b=\"urn:b\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:b\"/>\n" + " <prefix value=\"b\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n" + " <a:e>\n" + " <a:name>xxx</a:name>\n" + " </a:e>\n" + "</module>\n"; + + /* from YANG */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yang); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); + + /* context reset */ + ly_ctx_destroy(UTEST_LYCTX); + ly_ctx_new(NULL, 0, &UTEST_LYCTX); + + /* from YIN */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, &mod)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_test_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_test_yin); + free(printed); + + assert_non_null(mod = ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG, 0)); + assert_string_equal(printed, mod_def_yang); + free(printed); + + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YIN, 0)); + assert_string_equal(printed, mod_def_yin); + free(printed); + + /* invalid */ + mod_test_yang = "module x { namespace \"urn:x\"; prefix x; import a { prefix a; } a:e; }"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e/>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e name=\"xxx\"/>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e>\n" + " <x:name>xxx</x:name>\n" + " </a:e>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" element and its argument element \"name\" are expected in the same namespace, but they differ.", + NULL, 0); + + mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"x\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:x=\"urn:x\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:x\"/>\n" + " <prefix value=\"x\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e>\n" + " <a:value>xxx</a:value>\n" + " </a:e>\n" + "</module>\n"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL, 0); + CHECK_LOG_CTX("Extension instance \"a:e\" expects argument element \"name\" as its first XML child, but \"value\" element found.", + NULL, 0); + +} + +static void +test_extension_compile(void **state) +{ + struct lys_module *mod; + struct lysc_ctx cctx = {0}; + struct lysp_ext_instance ext_p = {0}; + struct lysp_ext_substmt *substmtp; + struct lysp_stmt child = {0}; + struct lysc_ext_instance ext_c = {0}; + struct lysc_ext_substmt *substmt; + LY_ERR rc = LY_SUCCESS; + + /* current module, whatever */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "yang"); + assert_true(mod); + + /* compile context */ + cctx.ctx = UTEST_LYCTX; + cctx.cur_mod = mod; + cctx.pmod = mod->parsed; + cctx.path_len = 1; + cctx.path[0] = '/'; + + /* parsed ext instance */ + lydict_insert(UTEST_LYCTX, "pref:my-ext", 0, &ext_p.name); + ext_p.format = LY_VALUE_JSON; + ext_p.parent_stmt = LY_STMT_MODULE; + + LY_ARRAY_NEW_GOTO(UTEST_LYCTX, ext_p.substmts, substmtp, rc, cleanup); + + substmtp->stmt = LY_STMT_ERROR_MESSAGE; + substmtp->storage = (uintptr_t)(void *)&ext_p.parsed; + /* fake parse */ + lydict_insert(UTEST_LYCTX, "my error", 0, (const char **)&ext_p.parsed); + + /* compiled ext instance */ + ext_c.parent_stmt = ext_p.parent_stmt; + LY_ARRAY_NEW_GOTO(UTEST_LYCTX, ext_c.substmts, substmt, rc, cleanup); + + substmt->stmt = LY_STMT_ERROR_MESSAGE; + substmt->storage = (uintptr_t)(void *)&ext_c.compiled; + + /* + * error-message + */ + ext_p.child = &child; + lydict_insert(UTEST_LYCTX, "error-message", 0, &child.stmt); + lydict_insert(UTEST_LYCTX, "my error", 0, &child.arg); + child.format = LY_VALUE_JSON; + child.kw = LY_STMT_ERROR_MESSAGE; + + /* compile */ + assert_int_equal(LY_SUCCESS, lyplg_ext_compile_extension_instance(&cctx, &ext_p, &ext_c)); + + /* check */ + assert_string_equal(ext_c.compiled, "my error"); + +cleanup: + lydict_remove(UTEST_LYCTX, ext_p.name); + lydict_remove(UTEST_LYCTX, child.stmt); + lydict_remove(UTEST_LYCTX, child.arg); + LY_ARRAY_FREE(ext_p.substmts); + lydict_remove(UTEST_LYCTX, ext_p.parsed); + LY_ARRAY_FREE(ext_c.substmts); + lydict_remove(UTEST_LYCTX, ext_c.compiled); + if (rc) { + fail(); + } +} + +static void +test_ext_recursive(void **state) +{ + const char *mod_base_yang, *mod_imp_yang, *mod_base_yin, *mod_imp_yin; + + mod_imp_yang = "module b {\n" + " namespace \"urn:b\";\n" + " prefix b;\n\n" + " extension use-in {\n" + " argument name {\n" + " b:arg-type {\n" + " type string;\n" + " }\n" + " }\n" + " b:use-in \"extension\";\n" + " b:occurence \"*\";\n" + " }\n" + "\n" + " extension substatement {\n" + " argument name {\n" + " b:arg-type {\n" + " type string;\n" + " }\n" + " }\n" + " b:use-in \"extension\";\n" + " b:occurence \"*\";\n" + " b:substatement \"b:occurence\";\n" + " }\n" + "\n" + " extension arg-type {\n" + " b:use-in \"argument\";\n" + " b:substatement \"type\" {\n" + " b:occurence \"1\";\n" + " }\n" + " b:substatement \"default\";\n" + " }\n" + "\n" + " extension occurence {\n" + " argument value {\n" + " b:arg-type {\n" + " type enumeration {\n" + " enum \"?\";\n" + " enum \"*\";\n" + " enum \"+\";\n" + " enum \"1\";\n" + " }\n" + " }\n" + " }\n" + " b:use-in \"extension\";\n" + " }\n" + "}\n"; + + mod_imp_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"b\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:b=\"urn:b\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:b\"/>\n" + " <prefix value=\"b\"/>\n" + " <import module=\"a\">\n" + " <prefix value=\"a\"/>\n" + " </import>\n\n" + " <a:e name=\"xxx\"/>\n" + "</module>\n"; + + mod_base_yang = "module a {\n" + " namespace \"urn:a\";\n" + " prefix a;\n\n" + " import b {\n" + " prefix b;\n" + " }\n" + "\n" + " extension abstract {\n" + " b:use-in \"identity\";\n" + " }\n" + "\n" + " identity mount-id;\n" + "\n" + " identity yang-lib-id {\n" + " base mount-id;\n" + " a:abstract;\n" + " }\n" + "}\n"; + + mod_base_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a=\"urn:a\">\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a\"/>\n\n" + " <extension name=\"e\">\n" + " <argument name=\"name\"/>\n" + " </extension>\n\n" + " <a:e name=\"aaa\"/>\n" + "</module>\n"; + + /* from YANG */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_imp_yang); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_base_yang, LYS_IN_YANG, NULL)); + + /* context reset */ + ly_ctx_destroy(UTEST_LYCTX); + ly_ctx_new(NULL, 0, &UTEST_LYCTX); + + /* from YIN */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_imp_yin); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, mod_base_yin, LYS_IN_YIN, NULL)); +} + +static void +test_lysc_path(void **state) +{ + const struct lysc_node *node; + char *path; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b;" + "container a {" + " list l {" + " key \"k l m\";" + " leaf k {type string;}" + " leaf l {type string;}" + " leaf m {type string;}" + " leaf n {type string;}" + " }" + "}}", LYS_IN_YANG, NULL)); + + node = lys_find_path(UTEST_LYCTX, NULL, "/b:a/l", 0); + path = lysc_path(node, LYSC_PATH_DATA_PATTERN, NULL, 0); + assert_string_equal(path, "/b:a/l[k='%s'][l='%s'][m='%s']"); + free(path); + node = lys_find_path(UTEST_LYCTX, NULL, "/b:a/l/n", 0); + path = lysc_path(node, LYSC_PATH_DATA_PATTERN, NULL, 0); + assert_string_equal(path, "/b:a/l[k='%s'][l='%s'][m='%s']/n"); + free(path); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_getnext), + UTEST(test_date), + UTEST(test_revisions), + UTEST(test_collision_typedef), + UTEST(test_collision_grouping), + UTEST(test_collision_identity), + UTEST(test_collision_feature), + UTEST(test_accessible_tree), + UTEST(test_includes), + UTEST(test_key_order), + UTEST(test_disabled_enum), + UTEST(test_identity), + UTEST(test_feature), + UTEST(test_extension_argument), + UTEST(test_extension_argument_element), + UTEST(test_extension_compile), + UTEST(test_ext_recursive), + UTEST(test_lysc_path), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c new file mode 100644 index 0000000..3d4fbf7 --- /dev/null +++ b/tests/utests/schema/test_tree_schema_compile.c @@ -0,0 +1,4110 @@ +/** + * @file test_tree_schema_compile.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for functions from parser_yang.c + * + * Copyright (c) 2018 - 2023 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "in.h" +#include "ly_common.h" +#include "parser_internal.h" +#include "path.h" +#include "plugins_types.h" +#include "schema_compile.h" +#include "xpath.h" + +static int +setup(void **state) +{ + UTEST_SETUP; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_DISABLE_SEARCHDIRS)); + + return 0; +} + +static void +test_imp_free_data(void *model_data, void *UNUSED(user_data)) +{ + free(model_data); +} + +static LY_ERR +test_imp_clb(const char *mod_name, const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + char *nl; + + if ((nl = strchr(user_data, '\n'))) { + /* more modules */ + if (!strncmp((char *)user_data + 7, mod_name, strlen(mod_name))) { + *module_data = strndup(user_data, nl - (char *)user_data); + *format = LYS_IN_YANG; + *free_module_data = test_imp_free_data; + } else { + *module_data = strdup(nl + 1); + *format = LYS_IN_YANG; + *free_module_data = test_imp_free_data; + } + } else { + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + } + return LY_SUCCESS; +} + +static void +test_module(void **state) +{ + const char *str, *feats[] = {"invalid", NULL}; + struct ly_in *in; + struct lys_module *mod = NULL; + struct lysp_feature *f; + struct lysc_iffeature *iff; + struct lys_glob_unres unres = {0}; + + str = "module test {namespace urn:test; prefix t;" + "feature f1;feature f2 {if-feature f1;}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + assert_int_equal(0, mod->implemented); + assert_int_equal(LY_EINVAL, lys_set_implemented(mod, feats)); + CHECK_LOG_CTX("Feature \"invalid\" not found in module \"test\".", NULL, 0); + assert_int_equal(LY_SUCCESS, lys_set_implemented(mod, NULL)); + assert_non_null(mod->compiled); + assert_string_equal("test", mod->name); + assert_string_equal("urn:test", mod->ns); + assert_string_equal("t", mod->prefix); + /* features */ + assert_non_null(mod->parsed->features); + assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->features)); + f = &mod->parsed->features[1]; + assert_non_null(f->iffeatures); + assert_int_equal(1, LY_ARRAY_COUNT(f->iffeatures)); + iff = &f->iffeatures_c[0]; + assert_non_null(iff->expr); + assert_non_null(iff->features); + assert_int_equal(1, LY_ARRAY_COUNT(iff->features)); + assert_ptr_equal(&mod->parsed->features[0], iff->features[0]); + + /* submodules cannot be compiled directly */ + str = "submodule test {belongs-to xxx {prefix x;}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL)); + lys_unres_glob_erase(&unres); + ly_in_free(in, 0); + CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0); + + /* data definition name collision in top level */ + str = "module aa {namespace urn:aa;prefix aa; leaf a {type string;} container a{presence x;}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); + assert_int_equal(LY_EEXIST, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod)); + ly_in_free(in, 0); + CHECK_LOG_CTX("Duplicate identifier \"/aa:a\" of data definition/RPC/action/notification statement.", "/aa:a", 0); +} + +static void +test_submodule(void **state) +{ + char *str; + + /* extension in a submodule */ + str = "submodule a-submod {yang-version 1.1; belongs-to a-mod {prefix a;}" + " extension ext2 {argument arg;}" + " typedef INTERFACE_NAME {type string; a:ext2 \"interface\";}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module a-mod {namespace urn:a-mod; prefix a; include a-submod;" + "identity baseid;" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); +} + +static void +test_name_collisions(void **state) +{ + const char *yang_data; + + /* top-level */ + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " leaf c {type empty;}" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:c", 0); + + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " notification c;" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:c", 0); + + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " rpc c;" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:c", 0); + + yang_data = "module a {namespace urn:a;prefix a;" + " container c;" + " leaf a {type empty;}" + " choice ch {" + " leaf c {type string;}" + " case c2 {" + " leaf aa {type empty;}" + " }" + " }" + "}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c\" of data definition/RPC/action/notification statement.", "/a:ch/c/c", 0); + + /* nested */ + yang_data = "module a {namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}" + "leaf-list a {type string;}" + "container a;" + "}}}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c/l/a\" of data definition/RPC/action/notification statement.", "/a:c/l/a", 0); + + yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}" + "leaf-list a {type string;}" + "notification a;" + "}}}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c/l/a\" of data definition/RPC/action/notification statement.", "/a:c/l/a", 0); + + yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}" + "leaf-list a {type string;}" + "action a;" + "}}}"; + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/a:c/l/a\" of data definition/RPC/action/notification statement.", "/a:c/l/a", 0); + + /* grouping */ +} + +static void +test_node_container(void **state) +{ + struct lys_module *mod; + struct lysc_node_container *cont; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;container c;}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((cont = (struct lysc_node_container *)mod->compiled->data)); + assert_int_equal(LYS_CONTAINER, cont->nodetype); + assert_string_equal("c", cont->name); + assert_true(cont->flags & LYS_CONFIG_W); + assert_true(cont->flags & LYS_STATUS_CURR); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;container c {config false; status deprecated; container child;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((cont = (struct lysc_node_container *)mod->compiled->data)); + assert_true(cont->flags & LYS_CONFIG_R); + assert_true(cont->flags & LYS_STATUS_DEPRC); + assert_non_null((cont = (struct lysc_node_container *)cont->child)); + assert_int_equal(LYS_CONTAINER, cont->nodetype); + assert_true(cont->flags & LYS_CONFIG_R); + assert_true(cont->flags & LYS_STATUS_DEPRC); + assert_string_equal("child", cont->name); +} + +static void +test_node_leaflist(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + struct lysc_node_leaflist *ll; + struct lysc_node_leaf *l; + const char *dflt; + uint8_t dynamic; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;" + "typedef mytype {type union {type leafref {path ../target;} type string;}}" + "leaf-list ll1 {type union {type decimal64 {fraction-digits 2;} type mytype;}}" + "leaf-list ll2 {type leafref {path ../target;}}" + "leaf target {type int8;}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype); + assert_non_null(((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype->basetype); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)type)->realtype->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c;typedef mytype {type int8;default 10;}" + "leaf-list ll1 {type mytype;default 1; default 1; config false;}" + "leaf-list ll2 {type mytype; ordered-by user;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data)); + assert_non_null(ll->dflts); + assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference (typedef's default does not reference own type) */ + assert_int_equal(2, LY_ARRAY_COUNT(ll->dflts)); + assert_string_equal("1", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("1", dflt = ll->dflts[1]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_DFLT | LYS_SET_CONFIG, ll->flags); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data->next)); + assert_non_null(ll->dflts); + assert_int_equal(6, ll->type->refcount); /* 3x type's reference, 3x default value's reference */ + assert_int_equal(1, LY_ARRAY_COUNT(ll->dflts)); + assert_string_equal("10", dflt = ll->dflts[0]->realtype->plugin->print(UTEST_LYCTX, ll->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, ll->flags); + + /* ordered-by is ignored (with verbose message) for state data, RPC/action output parameters and notification content */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {yang-version 1.1;namespace urn:d;prefix d;" + "leaf-list ll {config false; type string; ordered-by user;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data)); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_CONFIG, ll->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {yang-version 1.1;namespace urn:e;prefix e;" + "rpc oper {output {leaf-list ll {type string; ordered-by user;}}}}", LYS_IN_YANG, &mod)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1;namespace urn:f;prefix f;" + "notification event {leaf-list ll {type string; ordered-by user;}}}", LYS_IN_YANG, &mod)); + + /* forward reference in default */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {yang-version 1.1; namespace urn:g;prefix g;" + "leaf ref {type instance-identifier {require-instance true;} default \"/g:g[.='val']\";}" + "leaf-list g {type string;}}", LYS_IN_YANG, &mod)); + assert_non_null(l = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("ref", l->name); + assert_non_null(l->dflt); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf-list ll {type empty;}}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/aa:ll", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1;namespace urn:bb;prefix bb;leaf-list ll {type empty; default x;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "/bb:ll", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;" + "leaf-list ll {config false;type string; default one;default two;default one;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled); + assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data)); + assert_non_null(ll->dflts); + assert_int_equal(3, LY_ARRAY_COUNT(ll->dflts)); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;" + "leaf-list ll {type string; default one;default two;default one;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Configuration leaf-list has multiple defaults of the same value \"one\".", "/dd:ll", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;" + "leaf ref {type instance-identifier {require-instance true;} default \"/ee:g\";}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Invalid instance-identifier \"/ee:g\" value - semantic error: Not found node \"g\" in path.).", "/ee:ref", 0); +} + +static void +test_node_list(void **state) +{ + struct lys_module *mod; + struct lysc_node_list *list; + struct lysc_node *child; + struct ly_in *in; + const char *data = + "module a {namespace urn:a;prefix a;feature f;" + "list l1 {key \"x y\"; ordered-by user; leaf y{type string;if-feature f;} leaf x {type string; when 1;}}" + "list l2 {config false;leaf value {type string;}}}"; + const char *feats[] = {"f", NULL}; + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats, &mod)); + ly_in_free(in, 0); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_non_null(list->child); + assert_string_equal("x", list->child->name); + assert_true(list->child->flags & LYS_KEY); + assert_string_equal("y", list->child->next->name); + assert_true(list->child->next->flags & LYS_KEY); + assert_non_null(list->child); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, list->flags); + assert_true(list->child->flags & LYS_KEY); + assert_true(list->child->next->flags & LYS_KEY); + list = (struct lysc_node_list *)mod->compiled->data->next; + assert_non_null(list); + assert_non_null(list->child); + assert_false(list->child->flags & LYS_KEY); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_CONFIG | LYS_KEYLESS, list->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;" + "list l {key a; unique \"a c/b:b\"; unique \"c/e d\";" + "leaf a {type string; default x;} leaf d {type string;config false;}" + "container c {leaf b {type string;}leaf e{type string;config false;}}}}", + LYS_IN_YANG, &mod)); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_string_equal("l", list->name); + assert_string_equal("a", list->child->name); + assert_true(list->child->flags & LYS_KEY); + assert_null(((struct lysc_node_leaf *)list->child)->dflt); + assert_non_null(list->uniques); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques)); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[0])); + assert_string_equal("a", list->uniques[0][0]->name); + assert_true(list->uniques[0][0]->flags & LYS_UNIQUE); + assert_string_equal("b", list->uniques[0][1]->name); + assert_true(list->uniques[0][1]->flags & LYS_UNIQUE); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[1])); + assert_string_equal("e", list->uniques[1][0]->name); + assert_true(list->uniques[1][0]->flags & LYS_UNIQUE); + assert_string_equal("d", list->uniques[1][1]->name); + assert_true(list->uniques[1][1]->flags & LYS_UNIQUE); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c;" + "list l {key a;leaf a {type empty;}}}", LYS_IN_YANG, &mod)); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_string_equal("l", list->name); + assert_string_equal("a", list->child->name); + assert_true(list->child->flags & LYS_KEY); + assert_int_equal(LY_TYPE_EMPTY, ((struct lysc_node_leaf *)list->child)->type->basetype); + + /* keys order */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {yang-version 1.1;namespace urn:d;prefix d;" + "list l {key \"d b c\";leaf a {type string;} leaf b {type string;} leaf c {type string;} leaf d {type string;}}}", LYS_IN_YANG, &mod)); + list = (struct lysc_node_list *)mod->compiled->data; + assert_non_null(list); + assert_string_equal("l", list->name); + assert_non_null(child = list->child); + assert_string_equal("d", child->name); + assert_true(child->flags & LYS_KEY); + assert_non_null(child = child->next); + assert_string_equal("b", child->name); + assert_true(child->flags & LYS_KEY); + assert_non_null(child = child->next); + assert_string_equal("c", child->name); + assert_true(child->flags & LYS_KEY); + assert_non_null(child = child->next); + assert_string_equal("a", child->name); + assert_false(child->flags & LYS_KEY); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;list l;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Missing key in list representing configuration data.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1; namespace urn:bb;prefix bb;" + "list l {key x; leaf x {type string; when 1;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("List's key must not have any \"when\" statement.", "/bb:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;feature f;" + "list l {key x; leaf x {type string; if-feature f;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Key \"x\" is disabled.", "/cc:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;" + "list l {key x; leaf x {type string; config false;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Key of a configuration list must not be a state leaf.", "/dd:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;" + "list l {config false;key x; leaf x {type string; config true;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/ee:l/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;" + "list l {key x; leaf-list x {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("The list's key \"x\" not found.", "/ff:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;" + "list l {key x; unique y;leaf x {type string;} leaf-list y {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Unique's descendant-schema-nodeid \"y\" refers to leaf-list node instead of a leaf.", "/gg:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;" + "list l {key x; unique \"x y\";leaf x {type string;} leaf y {config false; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Unique statement \"x y\" refers to leaves with different config type.", "/hh:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;" + "list l {key x; unique a:x;leaf x {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"a:x\" - prefix \"a\" not defined in module \"ii\".", "/ii:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;" + "list l {key x; unique c/x;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c/x\" - target node not found.", "/jj:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;" + "list l {key x; unique c^y;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c^\" - missing \"/\" as node-identifier separator.", "/kk:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;" + "list l {key \"x y x\";leaf x {type string;}leaf y {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicated key identifier \"x\".", "/ll:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;" + "list l {key x;leaf x {type empty;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("List key of the \"empty\" type is allowed only in YANG 1.1 modules.", "/mm:l/x", 0); +} + +static void +test_node_choice(void **state) +{ + struct lys_module *mod; + struct lysc_node_choice *ch; + struct lysc_node_case *cs; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;feature f;" + "choice ch {default a:b; when \"true()\"; case a {leaf a1 {type string;}leaf a2 {type string;}}" + "leaf b {type string;}}}", LYS_IN_YANG, &mod)); + ch = (struct lysc_node_choice *)mod->compiled->data; + assert_non_null(ch); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, ch->flags); + assert_int_equal(1, LY_ARRAY_COUNT(ch->when)); + assert_null(ch->when[0]->context); + cs = ch->cases; + assert_non_null(cs); + assert_string_equal("a", cs->name); + assert_ptr_equal(ch, cs->parent); + assert_non_null(cs->child); + assert_string_equal("a1", cs->child->name); + assert_non_null(cs->child->next); + assert_string_equal("a2", cs->child->next->name); + assert_ptr_equal(cs, cs->child->parent); + cs = (struct lysc_node_case *)cs->next; + assert_non_null(cs); + assert_string_equal("b", cs->name); + assert_int_equal(LYS_STATUS_CURR | LYS_SET_DFLT | LYS_CONFIG_W, cs->flags); + assert_ptr_equal(ch, cs->parent); + assert_non_null(cs->child); + assert_string_equal("b", cs->child->name); + assert_ptr_equal(cs, cs->child->parent); + assert_ptr_equal(ch->dflt, cs); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "choice ch {case a {leaf x {type string;}}leaf x {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/aa:ch/a/x\" of data definition/RPC/action/notification statement.", "/aa:ch/x/x", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa;" + "choice ch {case a {leaf y {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/aa2:ch/a/y\" of data definition/RPC/action/notification statement.", "/aa2:ch/b/y", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;" + "choice ch {case a {leaf x {type string;}}leaf a {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:ch/a\" of case statement.", "/bb:ch/a", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb2 {namespace urn:bb2;prefix bb;" + "choice ch {case b {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb2:ch/b\" of case statement.", "/bb2:ch/b", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ca {namespace urn:ca;prefix ca;" + "choice ch {default c;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Default case \"c\" not found.", "/ca:ch", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cb {namespace urn:cb;prefix cb; import a {prefix a;}" + "choice ch {default a:a;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Default case \"a:a\" not found.", "/cb:ch", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;" + "choice ch {default a;case a {leaf x {mandatory true;type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Mandatory node \"x\" under the default case \"a\".", "/cc:ch", 0); + /* TODO check with mandatory nodes from augment placed into the case */ +} + +static void +test_node_anydata(void **state) +{ + struct lys_module *mod; + struct lysc_node_anydata *any; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;" + "anydata any {config false;mandatory true;}}", LYS_IN_YANG, &mod)); + any = (struct lysc_node_anydata *)mod->compiled->data; + assert_non_null(any); + assert_int_equal(LYS_ANYDATA, any->nodetype); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE | LYS_SET_CONFIG, any->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;" + "anyxml any;}", LYS_IN_YANG, &mod)); + any = (struct lysc_node_anydata *)mod->compiled->data; + assert_non_null(any); + assert_int_equal(LYS_ANYXML, any->nodetype); + assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, any->flags); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;anydata any;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"anydata\" as a child of \"module\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); +} + +static void +test_action(void **state) +{ + struct lys_module *mod; + const struct lysc_node_action *rpc; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;" + "rpc a {input {leaf x {type int8;} leaf y {type int8;}} output {leaf result {type int16;}}}}", LYS_IN_YANG, &mod)); + rpc = mod->compiled->rpcs; + assert_non_null(rpc); + assert_null(rpc->next); + assert_int_equal(LYS_RPC, rpc->nodetype); + assert_int_equal(LYS_STATUS_CURR, rpc->flags); + assert_string_equal("a", rpc->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {" + "action b {input {leaf x {type int8;} leaf y {type int8;}}" + "output {must \"result > 25\"; must \"/top\"; leaf result {type int16;}}}}" + "augment /top/b/output {leaf result2 {type string;}}}", LYS_IN_YANG, &mod)); + rpc = lysc_node_actions(mod->compiled->data); + assert_non_null(rpc); + assert_null(rpc->next); + assert_int_equal(LYS_ACTION, rpc->nodetype); + assert_int_equal(LYS_STATUS_CURR, rpc->flags); + assert_string_equal("b", rpc->name); + assert_null(rpc->input.musts); + assert_int_equal(2, LY_ARRAY_COUNT(rpc->output.musts)); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {action x;}}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} rpc x;}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:x\" of data definition/RPC/action/notification statement.", "/bb:x", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} action y;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/cc:c/y\" of data definition/RPC/action/notification statement.", "/cc:c/y", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {action z; action z;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/dd:c/z\" of data definition/RPC/action/notification statement.", "/dd:c/z", 0); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} notification w;}"); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; rpc w;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/ee:w\" of data definition/RPC/action/notification statement.", "/ee:w", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}" + "augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Action \"invalid\" is placed inside another RPC/action.", "/ff:{augment='/test/input/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}" + "augment /test/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/gg:{augment='/test/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; notification test {container a {uses grp;}}" + "grouping grp {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/hh:test/a/{uses='grp'}/invalid", 0); +} + +static void +test_notification(void **state) +{ + struct lys_module *mod; + const struct lysc_node_notif *notif; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;" + "notification a1 {leaf x {type int8;}} notification a2;}", LYS_IN_YANG, &mod)); + notif = mod->compiled->notifs; + assert_non_null(notif); + assert_non_null(notif->next); + assert_null(notif->next->next); + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("a1", notif->name); + assert_non_null(notif->child); + assert_string_equal("x", notif->child->name); + notif = notif->next; + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("a2", notif->name); + assert_null(notif->child); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b; container top {" + "notification b1 {leaf x {type int8;}} notification b2 {must \"/top\";}}}", LYS_IN_YANG, &mod)); + notif = lysc_node_notifs(mod->compiled->data); + assert_non_null(notif); + assert_non_null(notif->next); + assert_null(notif->next->next); + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("b1", notif->name); + assert_non_null(notif->child); + assert_string_equal("x", notif->child->name); + notif = notif->next; + assert_int_equal(LYS_NOTIF, notif->nodetype); + assert_int_equal(LYS_STATUS_CURR, notif->flags); + assert_string_equal("b2", notif->name); + assert_null(notif->child); + assert_int_equal(1, LY_ARRAY_COUNT(notif->musts)); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {notification x;}}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"notification\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} notification x;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:x\" of data definition/RPC/action/notification statement.", "/bb:x", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} notification y;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/cc:c/y\" of data definition/RPC/action/notification statement.", "/cc:c/y", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {notification z; notification z;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/dd:c/z\" of data definition/RPC/action/notification statement.", "/dd:c/z", 0); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}"); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; notification w;}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate identifier \"/ee:w\" of data definition/RPC/action/notification statement.", "/ee:w", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}" + "augment /test/input/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/ff:{augment='/test/input/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}" + "augment /test/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Notification \"invalid\" is placed inside another notification.", "/gg:{augment='/test/a'}/invalid", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; rpc test {input {container a {uses grp;}}}" + "grouping grp {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/hh:test/input/a/{uses='grp'}/invalid", 0); +} + +/** + * actually the same as length restriction (tested in test_type_length()), so just check the correct handling in appropriate types, + * do not test the expression itself + */ +static void +test_type_range(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf l {type int16 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INT16, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(-32768, ((struct lysc_type_num *)type)->range->parts[0].min_64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64); + assert_int_equal(32767, ((struct lysc_type_num *)type)->range->parts[1].min_64); + assert_int_equal(32767, ((struct lysc_type_num *)type)->range->parts[1].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;leaf l {type int32 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INT32, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(INT64_C(-2147483648), ((struct lysc_type_num *)type)->range->parts[0].min_64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64); + assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num *)type)->range->parts[1].min_64); + assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num *)type)->range->parts[1].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;leaf l {type int64 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INT64, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_num *)type)->range->parts[0].min_64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64); + assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num *)type)->range->parts[1].min_64); + assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num *)type)->range->parts[1].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;leaf l {type uint8 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT8, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(255, ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(255, ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;leaf l {type uint16 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT16, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(65535, ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(65535, ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;leaf l {type uint32 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT32, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;leaf l {type uint64 {range min..10|max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_UINT64, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(0, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num *)type)->range->parts[1].min_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num *)type)->range->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;typedef mytype {type uint8 {range 10..100;}}" + "typedef mytype2 {type mytype;} leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(3, type->refcount); + assert_int_equal(LY_TYPE_UINT8, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;" + "typedef mytype {type uint8 {range 1..100{description \"one to hundred\";reference A;}}}" + "leaf l {type mytype {range 1..10 {description \"one to ten\";reference B;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UINT8, type->basetype); + assert_non_null(((struct lysc_type_num *)type)->range); + assert_string_equal("one to ten", ((struct lysc_type_num *)type)->range->dsc); + assert_string_equal("B", ((struct lysc_type_num *)type)->range->ref); + assert_non_null(((struct lysc_type_num *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts)); + assert_int_equal(1, ((struct lysc_type_num *)type)->range->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_u64); +} + +static void +test_type_length(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type binary {length min {error-app-tag errortag;error-message error;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_string_equal("errortag", ((struct lysc_type_bin *)type)->length->eapptag); + assert_string_equal("error", ((struct lysc_type_bin *)type)->length->emsg); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf l {type binary {length max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;leaf l {type binary {length min..max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(0, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;leaf l {type binary {length 5;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(5, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(5, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;leaf l {type binary {length 1..10;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(1, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;leaf l {type binary {length 1..10|20..30;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(1, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + assert_int_equal(20, ((struct lysc_type_bin *)type)->length->parts[1].min_u64); + assert_int_equal(30, ((struct lysc_type_bin *)type)->length->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;leaf l {type binary {length \"16 | 32\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(16, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(16, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + assert_int_equal(32, ((struct lysc_type_bin *)type)->length->parts[1].min_u64); + assert_int_equal(32, ((struct lysc_type_bin *)type)->length->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;typedef mytype {type binary {length 10;}}" + "leaf l {type mytype {length \"10\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length \"50\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(50, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(50, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length \"10..30|60..100\";}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(30, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + assert_int_equal(60, ((struct lysc_type_bin *)type)->length->parts[1].min_u64); + assert_int_equal(100, ((struct lysc_type_bin *)type)->length->parts[1].max_u64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length \"10..80\";}}leaf ll {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(80, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(2, type->refcount); + assert_non_null(((struct lysc_type_bin *)type)->length); + assert_non_null(((struct lysc_type_bin *)type)->length->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_bin *)type)->length->parts)); + assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64); + assert_int_equal(100, ((struct lysc_type_bin *)type)->length->parts[0].max_u64); + + /* invalid values */ + assert_int_equal(LY_EDENIED, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf l {type binary {length -10;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - value \"-10\" does not fit the type limitations.", "/aa:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type binary {length 18446744073709551616;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/bb:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;leaf l {type binary {length \"max .. 10\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected data after max keyword (.. 10).", "/cc:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;leaf l {type binary {length 50..10;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/dd:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type binary {length \"50 | 10\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/ee:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type binary {length \"x\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected data (x).", "/ff:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type binary {length \"50 | min\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected data before min keyword (50 | ).", "/gg:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;leaf l {type binary {length \"| 50\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected beginning of the expression (| 50).", "/hh:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;leaf l {type binary {length \"10 ..\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression after \"..\" (10 ..).", "/ii:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;leaf l {type binary {length \".. 10\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected \"..\" without a lower bound.", "/jj:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;leaf l {type binary {length \"10 |\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression (10 |).", "/kk:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module kl {namespace urn:kl;prefix kl;leaf l {type binary {length \"10..20 | 15..30\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (15).", "/kl:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;typedef mytype {type binary {length 10;}}" + "leaf l {type mytype {length 11;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (11) is not equally or more limiting.", "/ll:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length 1..11;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (1..11) is not equally or more limiting.", "/mm:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length 20..110;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..110) is not equally or more limiting.", + "/nn:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;typedef mytype {type binary {length 10..100;}}" + "leaf l {type mytype {length 20..30|110..120;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..30|110..120) is not equally or more limiting.", + "/oo:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;typedef mytype {type binary {length 10..11;}}" + "leaf l {type mytype {length 15;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15) is not equally or more limiting.", "/pp:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module qq {namespace urn:qq;prefix qq;typedef mytype {type binary {length 10..20|30..40;}}" + "leaf l {type mytype {length 15..35;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15..35) is not equally or more limiting.", "/qq:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module rr {namespace urn:rr;prefix rr;typedef mytype {type binary {length 10;}}" + "leaf l {type mytype {length 10..35;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid length restriction - the derived restriction (10..35) is not equally or more limiting.", "/rr:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;leaf l {type binary {pattern '[0-9]*';}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid type restrictions for binary type.", "/ss:l", 0); +} + +static void +test_type_pattern(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;leaf l {type string {" + "pattern .* {error-app-tag errortag;error-message error;}" + "pattern [0-9].*[0-9] {modifier invert-match;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_non_null(((struct lysc_type_str *)type)->patterns); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->patterns)); + assert_string_equal("errortag", ((struct lysc_type_str *)type)->patterns[0]->eapptag); + assert_string_equal("error", ((struct lysc_type_str *)type)->patterns[0]->emsg); + assert_string_equal(".*", ((struct lysc_type_str *)type)->patterns[0]->expr); + assert_int_equal(0, ((struct lysc_type_str *)type)->patterns[0]->inverted); + assert_null(((struct lysc_type_str *)type)->patterns[1]->eapptag); + assert_null(((struct lysc_type_str *)type)->patterns[1]->emsg); + assert_string_equal("[0-9].*[0-9]", ((struct lysc_type_str *)type)->patterns[1]->expr); + assert_int_equal(1, ((struct lysc_type_str *)type)->patterns[1]->inverted); + + /* TODO check some data "^Å™$" */ +} + +static void +test_type_enum(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1; namespace urn:a;prefix a;feature f; leaf l {type enumeration {" + "enum automin; enum min {value -2147483648;}enum one {if-feature f; value 1;}" + "enum two; enum seven {value 7;}enum eight;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(5, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("automin", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(0, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("min", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(INT64_C(-2147483648), ((struct lysc_type_enum *)type)->enums[1].value); + assert_string_equal("two", ((struct lysc_type_enum *)type)->enums[2].name); + assert_int_equal(2, ((struct lysc_type_enum *)type)->enums[2].value); + assert_string_equal("seven", ((struct lysc_type_enum *)type)->enums[3].name); + assert_int_equal(7, ((struct lysc_type_enum *)type)->enums[3].value); + assert_string_equal("eight", ((struct lysc_type_enum *)type)->enums[4].name); + assert_int_equal(8, ((struct lysc_type_enum *)type)->enums[4].value); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1; namespace urn:b;prefix b;feature f; typedef mytype {type enumeration {" + "enum 11; enum min {value -2147483648;}enum x$&;" + "enum two; enum seven {value 7;}enum eight;}} leaf l { type mytype {enum seven;enum eight;}}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("seven", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(7, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("eight", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(8, ((struct lysc_type_enum *)type)->enums[1].value); + + const char *new_module = "module moc_c {yang-version 1.1; namespace urn:moc_c;prefix moc_c;feature f; typedef mytype {type enumeration {" + "enum first{value -270;} enum second; enum third {value -400;} enum fourth;}} leaf l { type mytype;}}"; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, new_module, LYS_IN_YANG, &mod)); + + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(4, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("first", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(-270, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("second", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(-269, ((struct lysc_type_enum *)type)->enums[1].value); + assert_string_equal("third", ((struct lysc_type_enum *)type)->enums[2].name); + assert_int_equal(-400, ((struct lysc_type_enum *)type)->enums[2].value); + assert_string_equal("fourth", ((struct lysc_type_enum *)type)->enums[3].name); + assert_int_equal(-268, ((struct lysc_type_enum *)type)->enums[3].value); + + new_module = "module moc_d {yang-version 1.1; namespace urn:moc_d;prefix moc_d;feature f; typedef mytype {type enumeration {" + "enum first; enum second;}} leaf l { type mytype;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, new_module, LYS_IN_YANG, &mod)); + + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_ENUM, type->basetype); + assert_non_null(((struct lysc_type_enum *)type)->enums); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_enum *)type)->enums)); + assert_string_equal("first", ((struct lysc_type_enum *)type)->enums[0].name); + assert_int_equal(0, ((struct lysc_type_enum *)type)->enums[0].value); + assert_string_equal("second", ((struct lysc_type_enum *)type)->enums[1].name); + assert_int_equal(1, ((struct lysc_type_enum *)type)->enums[1].value); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; feature f; leaf l {type enumeration {" + "enum one {if-feature f;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid keyword \"if-feature\" as a child of \"enum\" - the statement is allowed only in YANG 1.1 modules.", + NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum one {value -2147483649;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-2147483649\" of \"value\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum one {value 2147483648;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"2147483648\" of \"value\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum one; enum one;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate identifier \"one\" of enum statement.", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum '';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Enum name must not be zero-length.", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum ' x';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Enum name must not have any leading or trailing whitespaces (\" x\").", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum 'x ';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Enum name must not have any leading or trailing whitespaces (\"x \").", NULL, 1); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {" + "enum 'inva\nlid';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Control characters in enum name should be avoided (\"inva\nlid\", character number 5).", NULL, 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type enumeration;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing enum substatement for enumeration type.", "/bb:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;typedef mytype {type enumeration {enum one;}}" + "leaf l {type mytype {enum two;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - derived type adds new item \"two\".", "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;typedef mytype {type enumeration {enum one;}}" + "leaf l {type mytype {enum one {value 1;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - value of the item \"one\" has changed from 0 to 1 in the derived type.", "/dd:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type enumeration {enum x {value 2147483647;}enum y;}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - it is not possible to auto-assign enum value for \"y\" since the highest value is already 2147483647.", + "/ee:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type enumeration {enum x {value 1;}enum y {value 1;}}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid enumeration - value 1 collide in items \"y\" and \"x\".", "/ff:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;typedef mytype {type enumeration;}" + "leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing enum substatement for enumeration type mytype.", "/gg:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh; typedef mytype {type enumeration {enum one;}}" + "leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Enumeration type can be subtyped only in YANG 1.1 modules.", "/hh:l", 0); +} + +static void +test_type_dec64(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type decimal64 {" + "fraction-digits 2;range min..max;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_DEC64, type->basetype); + assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits); + assert_non_null(((struct lysc_type_dec *)type)->range); + assert_non_null(((struct lysc_type_dec *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts)); + assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_dec *)type)->range->parts[0].min_64); + assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_dec *)type)->range->parts[0].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;typedef mytype {type decimal64 {" + "fraction-digits 2;range '3.14 | 5.1 | 10';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_DEC64, type->basetype); + assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits); + assert_non_null(((struct lysc_type_dec *)type)->range); + assert_non_null(((struct lysc_type_dec *)type)->range->parts); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts)); + assert_int_equal(314, ((struct lysc_type_dec *)type)->range->parts[0].min_64); + assert_int_equal(314, ((struct lysc_type_dec *)type)->range->parts[0].max_64); + assert_int_equal(510, ((struct lysc_type_dec *)type)->range->parts[1].min_64); + assert_int_equal(510, ((struct lysc_type_dec *)type)->range->parts[1].max_64); + assert_int_equal(1000, ((struct lysc_type_dec *)type)->range->parts[2].min_64); + assert_int_equal(1000, ((struct lysc_type_dec *)type)->range->parts[2].max_64); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;typedef mytype {type decimal64 {" + "fraction-digits 2;range '1 .. 65535';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_int_equal(LY_TYPE_DEC64, type->basetype); + assert_int_equal(2, ((struct lysc_type_dec *)type)->fraction_digits); + assert_non_null(((struct lysc_type_dec *)type)->range); + assert_non_null(((struct lysc_type_dec *)type)->range->parts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_dec *)type)->range->parts)); + assert_int_equal(100, ((struct lysc_type_dec *)type)->range->parts[0].min_64); + assert_int_equal(6553500, ((struct lysc_type_dec *)type)->range->parts[0].max_64); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 0;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"0\" of \"fraction-digits\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits -1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"-1\" of \"fraction-digits\".", NULL, 1); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 19;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Value \"19\" is out of \"fraction-digits\" bounds.", NULL, 1); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ab {namespace urn:ab;prefix ab; typedef mytype {type decimal64;}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type mytype.", "/ab:l", 0); + + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type decimal64 {fraction-digits 2;" + "range '3.142';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Range boundary \"3.142\" of decimal64 type exceeds defined number (2) of fraction digits.", "/bb:l", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; leaf l {type decimal64 {fraction-digits 2;" + "range '4 | 3.14';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (3.14).", "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; typedef mytype {type decimal64 {fraction-digits 2;}}" + "leaf l {type mytype {fraction-digits 3;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.", "/dd:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module de {namespace urn:de;prefix de; typedef mytype {type decimal64 {fraction-digits 2;}}" + "typedef mytype2 {type mytype {fraction-digits 3;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid fraction-digits substatement for type \"mytype2\" not directly derived from decimal64 built-in type.", + "/de:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {" + "fraction-digits 18;range '-10 .. 0';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid range restriction - invalid value \"-10000000000000000000\".", "/ee:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {" + "fraction-digits 18;range '0 .. 10';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid range restriction - invalid value \"10000000000000000000\".", "/ee:l", 0); +} + +static void +test_type_instanceid(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;typedef mytype {type instance-identifier {require-instance false;}}" + "leaf l1 {type instance-identifier {require-instance true;}}" + "leaf l2 {type mytype;} leaf l3 {type instance-identifier;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INST, type->basetype); + assert_int_equal(1, ((struct lysc_type_instanceid *)type)->require_instance); + + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INST, type->basetype); + assert_int_equal(0, ((struct lysc_type_instanceid *)type)->require_instance); + + type = ((struct lysc_node_leaf *)mod->compiled->data->next->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_INST, type->basetype); + assert_int_equal(1, ((struct lysc_type_instanceid *)type)->require_instance); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {require-instance yes;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid value \"yes\" of \"require-instance\".", NULL, 1); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {fraction-digits 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid type restrictions for instance-identifier type.", "/aa:l", 0); +} + +static ly_bool +identity_isderived(const struct lysc_ident *base, const char *der) +{ + LY_ARRAY_COUNT_TYPE u; + + LY_ARRAY_FOR(base->derived, u) { + if (!strcmp(base->derived[u]->name, der)) { + return 1; + } + if (identity_isderived(base->derived[u], der)) { + return 1; + } + } + return 0; +} + +static ly_bool +contains_derived_identity(struct ly_ctx *ctx, char *module_name, + char *revision, char *identity_name, char *derived_name) +{ + LY_ARRAY_COUNT_TYPE u = 0; + struct lys_module *mod; + struct lysc_ident *identity = NULL; + + if (!(mod = ly_ctx_get_module(ctx, module_name, revision))) { + return 0; + } + + LY_ARRAY_FOR(mod->identities, u) { + if (!strcmp(identity_name, mod->identities[u].name)) { + identity = &mod->identities[u]; + break; + } + } + if (!identity) { + return 0; + } + + return identity_isderived(identity, derived_name); +} + +static void +test_identity(void **state) +{ + char *str; + const char *feats[2] = {NULL, NULL}; + struct lyd_node *tree; + const char *data; + +#define RESET_CTX(CTX) \ + ly_ctx_destroy(UTEST_LYCTX); \ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &UTEST_LYCTX)); + + /* It does not matter whether the base identity is in implemented + * module or not. + */ + + /* Implemented module's identity expand base identity located in unimplemented module. */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "identity id1 {base baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import a {prefix a;}" + "identity id2 {base a:baseid;}" + "leaf lf {type identityref {base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "/b:lf", 1); + assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); + assert_false(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + RESET_CTX(UTEST_LYCTX); + + /* Unimplemented module (c) expand base identity located in unimplemented module. */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "identity id1 {base baseid;}" + "}\n" + "module c {namespace urn:c; prefix c; import a {prefix a;}" + "identity id3 {base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import a {prefix a;} import c {prefix c;}" + "identity id2 {base a:baseid;}" + "leaf lf {type identityref {base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "/b:lf", 1); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:c\">ids:id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id3\" value - identity found in non-implemented module \"c\".", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + + /* Unimplemented module expand base identity located in implemented module. */ + str = "module b {namespace urn:b; prefix b;" + "identity baseid;" + "identity id2 {base baseid;}" + "leaf lf {type identityref {base baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module a {namespace urn:a; prefix a; import b {prefix b;}" + "identity id1 {base b:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + /* load (but don't implement) module (a) into context by module (c) */ + str = "module c {namespace urn:c; prefix c; import a {prefix a;}}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "b", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "b", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + + /* Transitivity of derived identity through unimplemented module. */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "identity id1 {base baseid;}" + "}\n" + "module c {namespace urn:c; prefix c; import a {prefix a;}" + "identity id3 {base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import c {prefix c;} import a {prefix a;}" + "identity id2 {base c:id3;}" + "leaf lf {type identityref {base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\">id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + RESET_CTX(UTEST_LYCTX); + + /* The base reference must not refer to a non-existent module, + * even if the module is not implemented. + */ + str = "module b {namespace urn:b; prefix b;" + "identity ident { base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + /* load (but don't implement) module (b) into context by module (c) */ + str = "module c {namespace urn:c; prefix c; import b {prefix b;}}"; + UTEST_INVALID_MODULE(str, LYS_IN_YANG, NULL, LY_EVALID); + RESET_CTX(UTEST_LYCTX); + + /* Tests in which multiple revisions are available and the import + * does not specify an exact revision. + */ + + /* The old revision was soon implemented + * and therefore its "baseid" is used. + */ + str = "module a {namespace urn:a; prefix a;" + "revision \"2014-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module a {namespace urn:a; prefix a;" + "revision \"2015-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b;" + "import a {prefix a;}" + "identity baseref { base a:baseid;}" + "identity id1 { base baseref;}" + "identity id2 { base baseref;}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "baseref")); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "id1")); + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id1</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "id2")); + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id2</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + RESET_CTX(UTEST_LYCTX); + + /* Even if a newer revision has been implemented, the old and + * unimplemented one will be used because it has already been + * imported. Therefore, if the user wants to use multiple revisions, + * he must choose one and implement it as soon as possible. + */ + str = "module a {namespace urn:a; prefix a;" + "revision \"2014-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b;" + "import a {prefix a;}" + "identity baseref { base a:baseid;}" + "identity id1 { base baseref;}" + "identity id2 { base baseref;}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module a {namespace urn:a; prefix a;" + "revision \"2015-05-08\";" + "identity baseid;" + "leaf alf { type identityref { base baseid;}}" + "}"; + ly_log_level(LY_LLVRB); + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + CHECK_LOG_LASTMSG("Implemented module \"a@2015-05-08\" was not and will not " + "be imported if the revision-date is missing in the import " + "statement. Instead, the revision \"2014-05-08\" is imported."); + ly_log_level(LY_LLWRN); + /* Data is inserted only to implemented revision. */ + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id1</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + data = "<alf xmlns=\"urn:a\" xmlns:ids=\"urn:b\">ids:id2</alf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", "2014-05-08", "baseid", "baseref")); + assert_false(contains_derived_identity(UTEST_LYCTX, "a", "2015-05-08", "baseid", "baseref")); + RESET_CTX(UTEST_LYCTX); + + /* Identity testing with if-features. */ + + /* The if-feature has no effect if the module is imported. */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "feature f;" + "identity baseid { if-feature \"f\";}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module b {namespace urn:b; prefix b; import a { prefix a;}" + "identity id1 { base a:baseid;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + RESET_CTX(UTEST_LYCTX); + + /* Even if the identity in the implemented module is disabled, + * it can be used as a base. + */ + str = "module a {yang-version 1.1; namespace urn:a; prefix a;" + "feature f;" + "identity baseid { if-feature \"f\";}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module b {namespace urn:b; prefix b; import a { prefix a;}" + "identity id1 { base a:baseid;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + RESET_CTX(UTEST_LYCTX); + + /* Identity derivation cannot be instantiated if it is disabled. + * Conversely, if the identity is enabled, it can be instantiated. + */ + str = "module a {namespace urn:a; prefix a;" + "identity baseid;" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + str = "module b {yang-version 1.1; namespace urn:b; prefix b; import a { prefix a;}" + "feature f2;" + "feature f3;" + "identity id1 { base a:baseid;}" + "identity id2 { if-feature \"f2\"; base a:baseid;}" + "identity id3 { if-feature \"f3\"; base a:baseid;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + feats[0] = "f2"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, feats, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\">id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\">id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\">id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"id3\" value - identity is disabled by if-feature.", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + + /* The derived identities are enabled and disabled in submodule. */ + str = "submodule asub {yang-version 1.1; belongs-to a {prefix a;}" + "feature f2;" + "feature f3;" + "identity id1 { base a:baseid;}" + "identity id2 { if-feature \"f2\"; base a:baseid;}" + "identity id3 { if-feature \"f3\"; base a:baseid;}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str); + str = "module a {namespace urn:a; prefix a; include asub;" + "identity baseid;" + "}"; + feats[0] = "f2"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, feats, NULL); + str = "module b {yang-version 1.1; namespace urn:b; prefix b; import a { prefix a;}" + "leaf lf { type identityref { base a:baseid;}}" + "}"; + UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id2</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); + lyd_free_tree(tree); + assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3")); + data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id3</lf>"; + CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); + CHECK_LOG_CTX("Invalid identityref \"ids:id3\" value - identity is disabled by if-feature.", "/b:lf", 1); + RESET_CTX(UTEST_LYCTX); + +#undef RESET_CTX +} + +static void +test_type_identityref(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;identity i; identity j; identity k {base i;}" + "typedef mytype {type identityref {base i;}}" + "leaf l1 {type mytype;} leaf l2 {type identityref {base a:k; base j;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_IDENT, type->basetype); + assert_non_null(((struct lysc_type_identityref *)type)->bases); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases)); + assert_string_equal("i", ((struct lysc_type_identityref *)type)->bases[0]->name); + + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_IDENT, type->basetype); + assert_non_null(((struct lysc_type_identityref *)type)->bases); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases)); + assert_string_equal("k", ((struct lysc_type_identityref *)type)->bases[0]->name); + assert_string_equal("j", ((struct lysc_type_identityref *)type)->bases[1]->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b;import a {prefix a;}" + "leaf l {type identityref {base a:k; base a:j;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_IDENT, type->basetype); + assert_non_null(((struct lysc_type_identityref *)type)->bases); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_identityref *)type)->bases)); + assert_string_equal("k", ((struct lysc_type_identityref *)type)->bases[0]->name); + assert_string_equal("j", ((struct lysc_type_identityref *)type)->bases[1]->name); + + /* invalid cases */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type identityref;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing base substatement for identityref type.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; typedef mytype {type identityref;}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing base substatement for identityref type mytype.", "/bb:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; identity i; typedef mytype {type identityref {base i;}}" + "leaf l {type mytype {base i;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid base substatement for the type not directly derived from identityref built-in type.", + "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; identity i; typedef mytype {type identityref {base i;}}" + "typedef mytype2 {type mytype {base i;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid base substatement for the type \"mytype2\" not directly derived from identityref built-in type.", + "/dd:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; identity i; identity j;" + "leaf l {type identityref {base i;base j;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Multiple bases in identityref type are allowed only in YANG 1.1 modules.", "/ee:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; identity i;leaf l {type identityref {base j;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Unable to find base (j) of identityref.", "/ff:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type identityref {base x:j;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid prefix used for base (x:j) of identityref.", "/gg:l", 0); +} + +static void +test_type_leafref(void **state) +{ + char *str; + struct lys_module *mod; + struct lysc_type *type; + const char *path; + struct lyxp_expr *expr; + + /* lys_path_parse() */ + path = "invalid_path"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath token \"NameTest\" (\"invalid_path\"), expected \"..\".", NULL, 0); + + path = ".."; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 0); + + path = "..["; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath token \"[\" (\"[\"), expected \"Operator(Path)\".", NULL, 0); + + path = "../"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 0); + + path = "/"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 0); + + path = "../../pref:id/xxx[predicate]/invalid!!!"; + assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + CHECK_LOG_CTX("Invalid character 0x21 ('!'), perhaps \"invalid\" is supposed to be a function call.", NULL, 0); + + path = "/absolute/prefix:path"; + assert_int_equal(LY_SUCCESS, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr)); + assert_int_equal(4, expr->used); + assert_int_equal(LYXP_TOKEN_OPER_PATH, expr->tokens[0]); + assert_int_equal(LYXP_TOKEN_NAMETEST, expr->tokens[1]); + assert_int_equal(LYXP_TOKEN_OPER_PATH, expr->tokens[2]); + assert_int_equal(LYXP_TOKEN_NAMETEST, expr->tokens[3]); + lyxp_expr_free(UTEST_LYCTX, expr); + + /* complete leafref paths */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a;" + "leaf ref1 {type leafref {path /a:target1;}} leaf ref2 {type leafref {path /a/target2; require-instance false;}}" + "leaf target1 {type string;}container a {leaf target2 {type uint8;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/a:target1", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "a", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/a/target2", ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_UINT8, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(0, ((struct lysc_type_leafref *)type)->require_instance); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; typedef mytype {type leafref {path /b:target;}}" + "typedef mytype2 {type mytype;} typedef mytype3 {type leafref {path /target;}} leaf ref {type mytype2;}" + "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type string;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + + /* prefixes are reversed to check using correct context of the path! */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix b; import b {prefix c;}" + "typedef mytype3 {type c:mytype {require-instance false;}}" + "leaf ref1 {type b:mytype3;}leaf ref2 {type c:mytype2;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_not_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(0, ((struct lysc_type_leafref *)type)->require_instance); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/b:target", ((struct lysc_type_leafref *)type)->path->expr); + assert_ptr_not_equal(mod, ly_resolve_prefix(UTEST_LYCTX, "b", 1, LY_VALUE_SCHEMA_RESOLVED, ((struct lysc_type_leafref *)type)->prefixes)); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + + /* non-prefixed nodes in path are supposed to be from the module where the leafref type is instantiated */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; import b {prefix b;}" + "leaf ref {type b:mytype3;}leaf target {type int8;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/target", ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_int_equal(1, ((struct lysc_type_leafref *)type)->require_instance); + + /* conditional leafrefs */ + str = "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1;" + "leaf ref1 {type leafref {path /target;}}" + "leaf target {if-feature 'f1'; type boolean;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Target of leafref \"ref1\" cannot be referenced because it is disabled.", "/e:ref1", 0); + CHECK_LOG_CTX("Not found node \"target\" in path.", "/e:ref1", 0); + + str = "module en {yang-version 1.1;namespace urn:en;prefix en;feature f1;" + "leaf ref1 {if-feature 'f1'; type leafref {path /target;}}" + "leaf target {type boolean;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod)); + + str = "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1;" + "leaf ref1 {if-feature 'f1'; type leafref {path /target;}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"target\" in path.", "/e:ref1", 0); + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module cl {namespace urn:cl;prefix cl;feature f1;" + "leaf f {type string; if-feature 'f1';}" + "leaf g {type leafref {path \"/cl:f\";}}" + "leaf h {type uint16; default 1;}}"); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module im {namespace urn:im;prefix im;import cl {prefix cl;}" + "leaf ref {must \"/cl:h > 0\"; type uint16;}}", LYS_IN_YANG, &mod)); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + CHECK_LOG_CTX("Target of leafref \"g\" cannot be referenced because it is disabled.", "/cl:g", 0); + CHECK_LOG_CTX("Not found node \"f\" in path.", "/cl:g", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;" + "list interface{key name;leaf name{type string;}list address {key ip;leaf ip {type string;}}}" + "container default-address{leaf ifname{type leafref{ path \"../../interface/name\";}}" + "leaf address {type leafref{ path \"../../interface[ name = current()/../ifname ]/address/ip\";}}}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)(*lysc_node_child_p(mod->compiled->data->prev))->prev)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("../../interface[ name = current()/../ifname ]/address/ip", + ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;" + "leaf source {type leafref {path \"/endpoint-parent[id=current()/../field]/endpoint/name\";}}" + "leaf field {type int32;}list endpoint-parent {key id;leaf id {type int32;}" + "list endpoint {key name;leaf name {type string;}}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("/endpoint-parent[id=current()/../field]/endpoint/name", ((struct lysc_type_leafref *)type)->path->expr); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_leafref *)type)->prefixes)); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref *)type)->realtype->basetype); + + /* leafref to imported (not yet implemented) module */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h-imp {namespace urn:h-imp;prefix h-imp;" + "leaf l {type string;}}", LYS_IN_YANG, NULL)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module h {namespace urn:h;prefix h;import h-imp {prefix hi;}" + "leaf h {type uint16;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;import h {prefix h;}" + "leaf i {type leafref {path /h:h;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_UINT16, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "h")); + assert_int_equal(1, mod->implemented); + assert_non_null(mod->compiled->data); + assert_string_equal("h", mod->compiled->data->name); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module j {namespace urn:j;prefix j; leaf j {type string;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;import j {prefix j;}" + "leaf i {type leafref {path \"/ilist[name = current()/../j:j]/value\";}}" + "list ilist {key name; leaf name {type string;} leaf value {type uint16;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_UINT16, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "j")); + assert_int_equal(1, mod->implemented); + assert_non_null(mod->compiled->data); + assert_string_equal("j", mod->compiled->data->name); + + /* leafref with a default value */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;" + "leaf source {type leafref {path \"../target\";}default true;}" + "leaf target {type boolean;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_LEAFREF, type->basetype); + assert_string_equal("../target", ((struct lysc_type_leafref *)type)->path->expr); + assert_non_null(((struct lysc_type_leafref *)type)->realtype); + assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref *)type)->realtype->basetype); + assert_non_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt); + assert_int_equal(LY_TYPE_BOOL, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->realtype->basetype); + assert_int_equal(1, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->boolean); + + /* union reference */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;" + "typedef s-ref {type union {type leafref {path '/str';}}}" + "leaf str {type string {length \"1..16\" {error-message \"Custom message\";}}}" + "leaf ref1 {type s-ref;}" + "leaf ref2 {type s-ref;}}", LYS_IN_YANG, NULL)); + + /* invalid paths */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"invalid\" in path.", "/aa:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path ../../toohigh;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Too many parent references in path.", "/bb:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path /a:invalid;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("No module connected with the prefix \"a\" found (prefix format schema stored mapping).", "/cc:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;leaf target1 {type string;}" + "container a {leaf target2 {type uint8;}} leaf ref1 {type leafref {" + "path '/a[target2 = current()/../target1]/target2';}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("List predicate defined for container \"a\" in path.", "/dd:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;\n container a {leaf target2 {type uint8;}}\n" + "leaf ref1 {type leafref {path /a!invalid;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"ee\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character 0x21 ('!'), perhaps \"a\" is supposed to be a function call.", NULL, 3); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;container a {leaf target2 {type uint8;}}" + "leaf ref1 {type leafref {path /a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid leafref path \"/a\" - target node is container instead of leaf or leaf-list.", "/ff:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;container a {leaf target2 {type uint8;" + "status deprecated;}} leaf ref1 {type leafref {path /a/target2;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("A current definition \"ref1\" is not allowed to reference deprecated definition \"target2\".", + "/gg:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;" + "leaf ref1 {type leafref;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing path substatement for leafref type.", "/hh:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;typedef mytype {type leafref;}" + "leaf ref1 {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing path substatement for leafref type mytype.", "/ii:ref1", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;" + "leaf ref {type leafref {path /target;}}leaf target {type string;config false;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid leafref path \"/target\" - target is supposed to represent configuration data (as the leafref does), but it does not.", + "/kk:ref", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;" + "leaf ref {type leafref {path /target; require-instance true;}}leaf target {type string;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.", + "/ll:ref", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type leafref {path /target;require-instance false;}}" + "leaf ref {type mytype;}leaf target {type string;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Leafref type \"mytype\" can be restricted by require-instance statement only in YANG 1.1 modules.", + "/mm:ref", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{\n path \"/interface[name is current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"nn\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character 0x69 ('i'), perhaps \"name\" is supposed to be a function call.", NULL, 5); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{\n path \"/interface[name=current()/../ifname/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"oo\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath expression end.", NULL, 5); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}" + "leaf ifname{type leafref{ path \"../interface/name\";}}" + "leaf address {type leafref{ path \"/interface[x:name=current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("No module connected with the prefix \"x\" found (prefix format schema stored mapping).", "/pp:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module qq {namespace urn:qq;prefix qq;" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}" + "leaf ifname{type leafref{ path \"../interface/name\";}}" + "leaf address {type leafref{ path \"/interface[id=current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"id\" in path.", "/qq:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module rr {namespace urn:rr;prefix rr;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name=current() / .. / ifname][name=current()/../test]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"rr\" failed.", NULL, 0); + CHECK_LOG_CTX("Duplicate predicate key \"name\" in path.", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = ../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"ss\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"FunctionName\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module tt {namespace urn:tt;prefix tt;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"tt\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"Operator(Path)\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module uu {namespace urn:uu;prefix uu;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/..ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"uu\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character 'i'[31] of expression '/interface[name = current()/..ifname]/ip'.", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module vv {namespace urn:vv;prefix vv;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"vv\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"NameTest\" (\"ifname]/ip\"), expected \"..\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ww {namespace urn:ww;prefix ww;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/../]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"ww\" failed.", NULL, 0); + CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]/ip\"), expected \"NameTest\".", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module xx {namespace urn:xx;prefix xx;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n" + "leaf address {type leafref{ path \"/interface[name = current()/../#node]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Parsing module \"xx\" failed.", NULL, 0); + CHECK_LOG_CTX("Invalid character '#'[32] of expression '/interface[name = current()/../#node]/ip'.", NULL, 4); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module yy {namespace urn:yy;prefix yy;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{ path \"/interface[name=current()/../x:ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("No module connected with the prefix \"x\" found (prefix format schema stored mapping).", "/yy:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zz {namespace urn:zz;prefix zz;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{ path \"/interface[name=current()/../xxx]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"xxx\" in path.", "/zz:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zza {namespace urn:zza;prefix zza;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}container c;\n" + "leaf address {type leafref{ path \"/interface[name=current()/../c]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Leaf expected instead of container \"c\" in leafref predicate in path.", "/zza:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzb {namespace urn:zzb;prefix zzb;\n" + "list interface{key name;leaf name{type string;}leaf ip {type string;}container c;}\n" + "leaf ifname{type leafref{ path \"../interface/name\";}}\n" + "leaf address {type leafref{ path \"/interface[c=current()/../ifname]/ip\";}}}", + LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Key expected instead of container \"c\" in path.", "/zzb:address", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzc {namespace urn:zzc;prefix zzc;\n" + "leaf source {type leafref {path \"../target\";}default true;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Not found node \"target\" in path.", "/zzc:source", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module zzd {namespace urn:zzd;prefix zzd;\n" + "leaf source {type leafref {path \"../target\";}default true;}\n" + "leaf target {type uint8;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid type uint8 value \"true\".).", "/zzd:source", 0); + + /* circular chain */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aaa {namespace urn:aaa;prefix aaa;\n" + "leaf ref1 {type leafref {path /ref2;}}\n" + "leaf ref2 {type leafref {path /ref3;}}\n" + "leaf ref3 {type leafref {path /ref4;}}\n" + "leaf ref4 {type leafref {path /ref1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid leafref path \"/ref1\" - circular chain of leafrefs detected.", "/aaa:ref4", 0); +} + +static void +test_type_empty(void **state) +{ + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "leaf l {type empty; default x;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;typedef mytype {type empty; default x;}" + "leaf l {type mytype;}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid type \"mytype\" - \"empty\" type must not have a default value (x).", "/bb:l", 0); +} + +static void +test_type_union(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {yang-version 1.1;namespace urn:a;prefix a; typedef mybasetype {type string;}" + "typedef mytype {type union {type int8; type mybasetype;}}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(2, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[1]->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b; typedef mybasetype {type string;}" + "typedef mytype {type union {type int8; type mybasetype;}}" + "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c; typedef mybasetype {type string;}" + "typedef mytype {type union {type leafref {path ../target;} type mybasetype;}}" + "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}" + "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type int8;}}", + LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(3, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[2]->basetype); + assert_non_null(((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype); + assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)((struct lysc_type_union *)type)->types[1])->realtype->basetype); + + /* invalid unions */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;typedef mytype {type union;}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing type substatement for union type mytype.", "/aa:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type union;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Missing type substatement for union type.", "/bb:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;typedef mytype {type union{type int8; type string;}}" + "leaf l {type mytype {type string;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid type substatement for the type not directly derived from union built-in type.", "/cc:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;typedef mytype {type union{type int8; type string;}}" + "typedef mytype2 {type mytype {type string;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid type substatement for the type \"mytype2\" not directly derived from union built-in type.", + "/dd:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type union{type mytype; type string;}}" + "leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ee:l", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;typedef mytype {type mytype2;}" + "typedef mytype2 {type mytype;} leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ef:l", 0); +} + +static void +test_type_dflt(void **state) +{ + struct lys_module *mod; + struct lysc_type *type; + struct lysc_node_leaf *leaf; + uint8_t dynamic; + + /* default is not inherited from union's types */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a; typedef mybasetype {type string;default hello;units xxx;}" + "leaf l {type union {type decimal64 {fraction-digits 2;} type mybasetype;}}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(1, type->refcount); + assert_int_equal(LY_TYPE_UNION, type->basetype); + assert_non_null(((struct lysc_type_union *)type)->types); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_union *)type)->types)); + assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union *)type)->types[0]->basetype); + assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union *)type)->types[1]->basetype); + assert_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt); + assert_null(((struct lysc_node_leaf *)mod->compiled->data)->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; typedef mybasetype {type string;default hello;units xxx;}" + "leaf l {type mybasetype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(3, type->refcount); /* 2x type reference, 1x default value's reference (typedf's default does not reference own type)*/ + assert_int_equal(LY_TYPE_STRING, type->basetype); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("xxx", leaf->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; typedef mybasetype {type string;default hello;units xxx;}" + "leaf l {type mybasetype; default goodbye;units yyy;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(3, type->refcount); /* 2x type reference, 1x default value's reference */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("yyy", leaf->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; typedef mybasetype {type string;default hello;units xxx;}" + "typedef mytype {type mybasetype;}leaf l1 {type mytype; default goodbye;units yyy;}" + "leaf l2 {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(6, type->refcount); /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_string_equal("goodbye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("yyy", leaf->units); + type = ((struct lysc_node_leaf *)mod->compiled->data->next)->type; + assert_non_null(type); + assert_int_equal(6, type->refcount); /* 4x type reference, 2x default value's reference (1 shared compiled type of typedefs which default does not reference own type) */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data->next; + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("xxx", leaf->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e; typedef mybasetype {type string;}" + "typedef mytype {type mybasetype; default hello;units xxx;}leaf l {type mytype;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(4, type->refcount); /* 3x type reference, 1x default value's reference (typedef's default does not reference own type) */ + assert_int_equal(LY_TYPE_STRING, type->basetype); + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("xxx", leaf->units); + + /* mandatory leaf does not takes default value from type */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;typedef mytype {type string; default hello;units xxx;}" + "leaf l {type mytype; mandatory true;}}", LYS_IN_YANG, &mod)); + type = ((struct lysc_node_leaf *)mod->compiled->data)->type; + assert_non_null(type); + assert_int_equal(LY_TYPE_STRING, type->basetype); + assert_null(((struct lysc_node_leaf *)mod->compiled->data)->dflt); + assert_string_equal("xxx", ((struct lysc_node_leaf *)mod->compiled->data)->units); +} + +static void +test_type_exts(void **state) +{ + const char *schema1, *schema2, *schema3, *schema4; + struct lys_module *mod; + const struct lysc_node *snode; + struct lysc_type *type; + struct lysc_type_union *type_u; + + schema1 = "module my-extensions {\n" + " namespace \"urn:my-extensions\";\n" + " prefix my-ext;\n" + "\n" + " extension shortdesc {\n" + " argument shortdsc;\n" + " }\n" + "}\n"; + schema2 = "module module-inet {\n" + " yang-version 1.1;\n" + " namespace \"urn:module-inet\";\n" + " prefix mod-inet;\n" + "\n" + " import ietf-inet-types {\n" + " prefix inet;\n" + " }\n" + "\n" + " import my-extensions {\n" + " prefix my-ext;\n" + " }\n" + "\n" + " typedef domain-name {\n" + " type inet:domain-name {\n" + " my-ext:shortdesc \"<host-name>\";\n" + " }\n" + " }\n" + "\n" + " typedef ipv4-address {\n" + " type inet:ipv4-address-no-zone {\n" + " my-ext:shortdesc \"<A.B.C.D>\";\n" + " }\n" + " }\n" + " typedef my-enum {\n" + " type enumeration {\n" + " enum one;\n" + " enum two;\n" + " enum three;\n" + " }\n" + " }\n" + "}\n"; + schema3 = "module module-a {\n" + " yang-version 1.1;\n" + " namespace \"urn:module-a\";\n" + " prefix mod-a;\n" + "\n" + " import module-inet {\n" + " prefix mod-inet;\n" + " }\n" + "\n" + " import my-extensions {\n" + " prefix my-ext;\n" + " }\n" + "\n" + " typedef server-address {\n" + " type union {\n" + " type mod-inet:ipv4-address {\n" + " my-ext:shortdesc \"<ipv4-address>\";\n" + " }\n" + " type mod-inet:domain-name {\n" + " my-ext:shortdesc \"<fqdn>\";\n" + " }\n" + " }\n" + " }\n" + "}\n"; + schema4 = "module main-module {\n" + " yang-version 1.1;\n" + " namespace \"urn:main-module\";\n" + " prefix main;\n" + "\n" + " import module-a {\n" + " prefix mod-a;\n" + " }\n" + "\n" + " import module-inet {\n" + " prefix mod-inet;\n" + " }\n" + "\n" + " import my-extensions {\n" + " prefix my-ext;\n" + " }\n" + "\n" + " container config {\n" + " leaf server {\n" + " type mod-a:server-address {\n" + " my-ext:shortdesc \"<server-address>\";\n" + " }\n" + " }\n" + "\n" + " leaf hostname {\n" + " type union {\n" + " type mod-inet:domain-name;\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + "\n" + " leaf my-leaf {\n" + " type mod-inet:my-enum {\n" + " my-ext:shortdesc \"my enum\";\n" + " }\n" + " }\n" + "}\n"; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema1, LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema2, LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema3, LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema4, LYS_IN_YANG, &mod)); + + /* server */ + snode = lys_find_path(UTEST_LYCTX, NULL, "/main-module:config/server", 0); + assert_non_null(snode); + + type = ((struct lysc_node_leaf *)snode)->type; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 1); + assert_string_equal(type->exts[0].argument, "<server-address>"); + type_u = (struct lysc_type_union *)type; + assert_int_equal(LY_ARRAY_COUNT(type_u->types), 2); + + type = type_u->types[0]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 2); + assert_string_equal(type->exts[0].argument, "<A.B.C.D>"); + assert_string_equal(type->exts[1].argument, "<ipv4-address>"); + + type = type_u->types[1]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 2); + assert_string_equal(type->exts[0].argument, "<host-name>"); + assert_string_equal(type->exts[1].argument, "<fqdn>"); + + /* hostname */ + snode = lys_find_path(UTEST_LYCTX, NULL, "/main-module:config/hostname", 0); + assert_non_null(snode); + type = ((struct lysc_node_leaf *)snode)->type; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 0); + type_u = (struct lysc_type_union *)type; + assert_int_equal(LY_ARRAY_COUNT(type_u->types), 2); + + type = type_u->types[0]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 1); + assert_string_equal(type->exts[0].argument, "<host-name>"); + + type = type_u->types[1]; + assert_int_equal(LY_ARRAY_COUNT(type->exts), 0); +} + +static void +test_status(void **state) +{ + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "container c {status deprecated; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"deprecated\" status of parent \"c\".", "/aa:c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;" + "container c {status obsolete; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/bb:c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;" + "container c {status obsolete; leaf l {status deprecated; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Status \"deprecated\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/cc:c/l", 0); + + /* just a warning */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:dd;prefix d;" + "container c {leaf l {status obsolete; type string;}}" + "container d {leaf m {when \"../../c/l\"; type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition \"../../c/l\" may be referencing deprecated node \"l\".", NULL, 0); +} + +static void +test_grouping(void **state) +{ + /* result ok, but a warning about not used locally scoped grouping printed */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a; grouping grp1 {leaf a1 {type string;}}" + "container a {leaf x {type string;} grouping grp2 {leaf a2 {type string;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Locally scoped grouping \"grp2\" not used.", NULL, 0); + + /* result ok - when statement or leafref target must be checked only at the place where the grouping is really instantiated */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b; grouping grp {" + "leaf ref {type leafref {path \"../name\";}}" + "leaf cond {type string; when \"../name = 'specialone'\";}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX(NULL, NULL, 0); + + /* invalid - error in a non-instantiated grouping */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "grouping grp {leaf x {type leafref;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:{grouping='grp'}/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;" + "container a {grouping grp {leaf x {type leafref;}}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:a/{grouping='grp'}/x", 0); + CHECK_LOG_CTX("Locally scoped grouping \"grp\" not used.", NULL, 0); + + /* config check */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module z1 {namespace urn:z1;prefix z1;" + "container root;}\n" + "module z2 {namespace urn:z2;prefix z2;" + "grouping leafs_group {" + " leaf name {type string; config true;}" + " leaf value {type uint32; config true;}" + "}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module z3 {namespace urn:z3;prefix z3;" + "import z1 {prefix z1;} import z2 {prefix z2;}" + "grouping grp_a_top {leaf a1 {type int8;}}" + "grouping list_group {" + " list mylist {key \"name\"; unique \"value\"; uses z2:leafs_group;}" + "}" + "augment /z1:root { uses list_group;} }", LYS_IN_YANG, NULL)); + + /* identity */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module y1 {namespace urn:y1;prefix y1;" + "identity base_identity;" + "identity id1 {base \"base_identity\";}" + "grouping attrs_group {" + " leaf name {type identityref {base \"base_identity\";} default \"id1\";}" + "}}", LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module y2 {namespace urn:y2;prefix y2;" + "import y1 {prefix y1;}" + "container root {uses y1:attrs_group;}}", LYS_IN_YANG, NULL)); +} + +static void +test_uses(void **state) +{ + struct lys_module *mod; + const struct lysc_node *parent, *child; + const struct lysc_node_container *cont; + const struct lysc_node_leaf *leaf; + const struct lysc_node_choice *choice; + const struct lysc_node_case *cs; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module grp {namespace urn:grp;prefix g; typedef mytype {type string;} feature f;" + "grouping grp {leaf x {type mytype;} leaf y {type string; if-feature f;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;import grp {prefix g;}" + "grouping grp_a_top {leaf a1 {type int8;}}" + "container a {uses grp_a; uses grp_a_top; uses g:grp; grouping grp_a {leaf a2 {type uint8;}}}}", LYS_IN_YANG, &mod)); + assert_non_null((parent = mod->compiled->data)); + assert_int_equal(LYS_CONTAINER, parent->nodetype); + assert_non_null((child = ((struct lysc_node_container *)parent)->child)); + assert_string_equal("a2", child->name); + assert_ptr_equal(mod, child->module); + assert_non_null((child = child->next)); + assert_string_equal("a1", child->name); + assert_ptr_equal(mod, child->module); + assert_non_null((child = child->next)); + assert_string_equal("x", child->name); + assert_ptr_equal(mod, child->module); + assert_null((child = child->next)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule bsub {belongs-to b {prefix b;} grouping grp {leaf b {when 1; type string;} leaf c {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;include bsub;uses grp {when 2;}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + + leaf = (struct lysc_node_leaf *)mod->compiled->data; + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("b", leaf->name); + assert_int_equal(2, LY_ARRAY_COUNT(leaf->when)); + assert_int_equal(1, leaf->when[0]->refcount); + assert_non_null(leaf->when[0]->context); + assert_string_equal("b", leaf->when[0]->context->name); + assert_int_equal(2, leaf->when[1]->refcount); + assert_null(leaf->when[1]->context); + + leaf = (struct lysc_node_leaf *)leaf->next; + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("c", leaf->name); + assert_int_equal(1, LY_ARRAY_COUNT(leaf->when)); + assert_int_equal(2, leaf->when[0]->refcount); + assert_null(leaf->when[0]->context); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;" + "grouping grp {leaf l {type string;}leaf k {type string; status obsolete;}}" + "uses grp {status deprecated;}}", LYS_IN_YANG, &mod)); + assert_int_equal(LYS_LEAF, mod->compiled->data->nodetype); + assert_string_equal("l", mod->compiled->data->name); + assert_true(LYS_STATUS_DEPRC & mod->compiled->data->flags); + assert_int_equal(LYS_LEAF, mod->compiled->data->next->nodetype); + assert_string_equal("k", mod->compiled->data->next->name); + assert_true(LYS_STATUS_OBSLT & mod->compiled->data->next->flags); + CHECK_LOG_CTX(NULL, NULL, 0); /* no warning about inheriting deprecated flag from uses */ + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; grouping grp {container g;}" + "container top {uses grp {augment g {leaf x {type int8;}}}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + assert_non_null(child = lysc_node_child(mod->compiled->data)); + assert_string_equal("g", child->name); + assert_non_null(child = lysc_node_child(child)); + assert_string_equal("x", child->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {yang-version 1.1;namespace urn:e;prefix e; grouping grp {action g { description \"super g\";}}" + "container top {action e; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + cont = (const struct lysc_node_container *)mod->compiled->data; + assert_non_null(cont->actions); + assert_non_null(cont->actions->next); + assert_null(cont->actions->next->next); + assert_string_equal("e", cont->actions->next->name); + assert_string_equal("g", cont->actions->name); + assert_string_equal("ultra g", cont->actions->dsc); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1;namespace urn:f;prefix f; grouping grp {notification g { description \"super g\";}}" + "container top {notification f; uses grp {refine g {description \"ultra g\";}}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + cont = (const struct lysc_node_container *)mod->compiled->data; + assert_non_null(cont->notifs); + assert_non_null(cont->notifs->next); + assert_null(cont->notifs->next->next); + assert_string_equal("f", cont->notifs->next->name); + assert_string_equal("g", cont->notifs->name); + assert_string_equal("ultra g", cont->notifs->dsc); + + /* empty grouping */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g; grouping grp; uses grp;}", LYS_IN_YANG, &mod)); + assert_null(mod->compiled->data); + + /* choice in uses */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {yang-version 1.1;namespace urn:h;prefix h; grouping grp {choice gch {case gc1 { leaf y { type string;}} case gc2 {leaf z {type string;}}}}" + "choice ch {case one { leaf x {type string;}} case two { uses grp;}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + choice = (const struct lysc_node_choice *)mod->compiled->data; + assert_string_equal("ch", choice->name); + cs = choice->cases; + assert_non_null(cs); + assert_string_equal("one", cs->name); + assert_non_null(cs->child); + assert_string_equal("x", cs->child->name); + + cs = (struct lysc_node_case *)cs->next; + assert_non_null(cs); + assert_string_equal("two", cs->name); + assert_non_null(cs->child); + assert_string_equal("gch", cs->child->name); + + /* top-level uses with augment and refine */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i; grouping grp {container g;}" + "uses grp {augment g {leaf x {type int8;}} refine g {description \"dsc\";}}}", + LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + child = mod->compiled->data; + assert_string_equal("g", child->name); + cont = (const struct lysc_node_container *)child; + assert_string_equal("dsc", cont->dsc); + assert_non_null(child = lysc_node_child(child)); + assert_string_equal("x", child->name); + + /* unique */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module j {namespace urn:j;prefix j;" + "grouping grp {list l {key \"k\"; unique \"l\"; leaf k {type string;} leaf l {type string;}}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;import j {prefix j;}" + "container a {uses j:grp;}}", LYS_IN_YANG, NULL)); + + /* if-features */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;" + "feature f;" + "grouping grp {container g; leaf l{type string;}}" + "uses grp {if-feature f;}}", + LYS_IN_YANG, &mod)); + assert_null(mod->compiled->data); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Grouping \"missinggrp\" referenced by a uses statement not found.", "/aa:{uses='missinggrp'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;uses grp;" + "grouping grp {leaf a{type string;}uses grp1;}" + "grouping grp1 {leaf b {type string;}uses grp2;}" + "grouping grp2 {leaf c {type string;}uses grp;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Grouping \"grp\" references itself through a uses statement.", + "/bb:{uses='grp'}/{uses='grp1'}/{uses='grp2'}/{uses='grp'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;uses a:missingprefix;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid prefix used for grouping \"a:missingprefix\" reference.", "/cc:{uses='a:missingprefix'}", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;grouping grp{leaf a{type string;}}" + "leaf a {type string;}uses grp;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/dd:a\" of data definition/RPC/action/notification statement.", + "/dd:{uses='grp'}/dd:a", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;grouping grp {leaf l {type string; status deprecated;}}" + "uses grp {status obsolete;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".", + "/ee:{uses='grp'}/ee:l", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;grouping grp {leaf l {type string;}}" + "leaf l {type int8;}uses grp;}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/ff:l\" of data definition/RPC/action/notification statement.", + "/ff:{uses='grp'}/ff:l", 0); + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module fg {namespace urn:fg;prefix fg;grouping grp {leaf m {type string;}}" + "uses grp;leaf m {type int8;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/fg:m\" of data definition/RPC/action/notification statement.", "/fg:m", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; grouping grp {container g;}" + "leaf g {type string;}" + "container top {uses grp {augment /g {leaf x {type int8;}}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"/g\" - name test expected instead of \"/\".", + "/gg:top/{uses='grp'}/{augment='/g'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1;namespace urn:hh;prefix hh;" + "grouping grp {notification g { description \"super g\";}}" + "container top {notification h; uses grp {refine h {description \"ultra h\";}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Refine(s) target node \"h\" in grouping \"grp\" was not found.", "/hh:top/{uses='grp'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module ii {yang-version 1.1;namespace urn:ii;prefix ii;" + "grouping grp {action g { description \"super g\";}}" + "container top {action i; uses grp {refine i {description \"ultra i\";}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Refine(s) target node \"i\" in grouping \"grp\" was not found.", "/ii:top/{uses='grp'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {yang-version 1.1;namespace urn:jj;prefix jj;" + "grouping grp {leaf j { when \"1\"; type invalid;}}" + "container top {uses grp;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Referenced type \"invalid\" not found.", "/jj:top/{uses='grp'}/j", 0); +} + +static void +test_refine(void **state) +{ + struct lys_module *mod; + struct lysc_node *parent, *child; + struct lysc_node_leaf *leaf; + struct lysc_node_leaflist *llist; + uint8_t dynamic; + struct ly_in *in; + const char *data, *feats1[] = {"f", NULL}, *feats2[] = {"fa", NULL}; + + data = "module grp {yang-version 1.1;namespace urn:grp;prefix g; feature f;typedef mytype {type string; default cheers!;}" + "grouping grp {container c {leaf l {type mytype; default goodbye;}" + "leaf-list ll {type mytype; default goodbye; max-elements 6;}" + "choice ch {default ca; leaf ca {type int8;}leaf cb{type uint8;}}" + "leaf x {type mytype; mandatory true; must 1;}" + "anydata a {mandatory false; if-feature f; description original; reference original;}" + "container c {config false; leaf l {type string;}}}}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats1, NULL)); + + data = "module a {yang-version 1.1;namespace urn:a;prefix a;import grp {prefix g;}feature fa;" + "uses g:grp {refine c/l {default hello; config false;}" + "refine c/ll {default hello;default world;}" + "refine c/ch {default cb;config true; if-feature fa;}" + "refine c/x {mandatory false; must ../ll;description refined; reference refined;}" + "refine c/a {mandatory true; must 1; description refined; reference refined;}" + "refine c/ll {max-elements 5;}" + "refine c/c {config true;presence indispensable;}}}"; + ly_in_memory(in, data); + assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, feats2, &mod)); + ly_in_free(in, 0); + + assert_non_null((parent = mod->compiled->data)); + assert_int_equal(LYS_CONTAINER, parent->nodetype); + assert_string_equal("c", parent->name); + assert_non_null((leaf = (struct lysc_node_leaf *)((struct lysc_node_container *)parent)->child)); + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("l", leaf->name); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(LYS_CONFIG_R, leaf->flags & LYS_CONFIG_MASK); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(LYS_LEAFLIST, llist->nodetype); + assert_string_equal("ll", llist->name); + assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("world", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(5, llist->max); + assert_non_null(child = llist->next); + assert_int_equal(LYS_CHOICE, child->nodetype); + assert_string_equal("ch", child->name); + assert_string_equal("cb", ((struct lysc_node_choice *)child)->dflt->name); + assert_true(LYS_SET_DFLT & ((struct lysc_node_choice *)child)->dflt->flags); + assert_false(LYS_SET_DFLT & ((struct lysc_node_choice *)child)->cases[0].flags); + assert_non_null(leaf = (struct lysc_node_leaf *)child->next); + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("x", leaf->name); + assert_false(LYS_MAND_TRUE & leaf->flags); + assert_string_equal("cheers!", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(leaf->musts); + assert_int_equal(2, LY_ARRAY_COUNT(leaf->musts)); + assert_string_equal("refined", leaf->dsc); + assert_string_equal("refined", leaf->ref); + assert_non_null(child = leaf->next); + assert_int_equal(LYS_ANYDATA, child->nodetype); + assert_string_equal("a", child->name); + assert_true(LYS_MAND_TRUE & child->flags); + assert_non_null(((struct lysc_node_anydata *)child)->musts); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_node_anydata *)child)->musts)); + assert_string_equal("refined", child->dsc); + assert_string_equal("refined", child->ref); + assert_non_null(child = child->next); + assert_int_equal(LYS_CONTAINER, child->nodetype); + assert_string_equal("c", child->name); + assert_true(LYS_PRESENCE & child->flags); + assert_true(LYS_CONFIG_W & child->flags); + assert_true(LYS_CONFIG_W & ((struct lysc_node_container *)child)->child->flags); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {yang-version 1.1;namespace urn:b;prefix b;import grp {prefix g;}" + "uses g:grp {status deprecated; refine c/x {default hello; mandatory false;}}}", LYS_IN_YANG, &mod)); + assert_non_null((leaf = (struct lysc_node_leaf *)((struct lysc_node_container *)mod->compiled->data)->child->prev->prev->prev)); + assert_int_equal(LYS_LEAF, leaf->nodetype); + assert_string_equal("x", leaf->name); + assert_false(LYS_MAND_TRUE & leaf->flags); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c;" + "grouping alg {leaf alg2 {type bits {" + "bit ftp;bit h323-q931;bit h323-ras;bit pptp;bit rtsp;bit sip-tcp;bit sip-udp;bit tftp;bit dns-udp;}}}" + "container conf {uses alg {refine alg2 {default \"dns-udp\";}}}}", LYS_IN_YANG, &mod)); + + /* invalid */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;import grp {prefix g;}" + "uses g:grp {refine c {default hello;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of container node - it is not possible to replace \"default\" property.", + "/aa:{uses='g:grp'}/aa:c/{refine='c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import grp {prefix g;}" + "uses g:grp {refine c/l {default hello; default world;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf with too many (2) default properties.", "/bb:{uses='g:grp'}/bb:c/l/{refine='c/l'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;import grp {prefix g;}" + "uses g:grp {refine c/ll {default hello; default world;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of default in leaf-list - the default statement is allowed only in YANG 1.1 modules.", + "/cc:{uses='g:grp'}/cc:c/ll/{refine='c/ll'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;import grp {prefix g;}" + "uses g:grp {refine c/ll {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf-list node - it is not possible to replace \"mandatory\" property.", + "/dd:{uses='g:grp'}/dd:c/ll/{refine='c/ll'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;import grp {prefix g;}" + "uses g:grp {refine c/l {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ee:{uses='g:grp'}/ee:c/l", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/ee:{uses='g:grp'}/ee:c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;import grp {prefix g;}" + "uses g:grp {refine c/ch {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ef:{uses='g:grp'}/ef:c/ch", 0); + CHECK_LOG_CTX("Invalid mandatory choice with a default case.", "/ef:{uses='g:grp'}/ef:c/ch", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;import grp {prefix g;}" + "uses g:grp {refine c/ch/ca/ca {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Mandatory node \"ca\" under the default case \"ca\".", "/ff:{uses='g:grp'}/ff:c/ch", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;import grp {prefix g;}" + "uses g:grp {refine c/x {default hello;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg:{uses='g:grp'}/gg:c/x", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/gg:{uses='g:grp'}/gg:c/x", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import grp {prefix g;}" + "uses g:grp {refine c/c/l {config true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/hh:{uses='g:grp'}/hh:c/c/l", 0); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/hh:{uses='g:grp'}/hh:c/c/l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;grouping grp {leaf l {type string; status deprecated;}}" + "uses grp {status obsolete;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".", + "/ii:{uses='grp'}/ii:l", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;import grp {prefix g;}" + "uses g:grp {refine c/x {presence nonsence;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"presence\" property.", + "/jj:{uses='g:grp'}/jj:c/x/{refine='c/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;import grp {prefix g;}" + "uses g:grp {refine c/ch {must 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of choice node - it is not possible to add \"must\" property.", + "/kk:{uses='g:grp'}/kk:c/ch/{refine='c/ch'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;import grp {prefix g;}" + "uses g:grp {refine c/x {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"min-elements\" property.", + "/ll:{uses='g:grp'}/ll:c/x/{refine='c/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;import grp {prefix g;}" + "uses g:grp {refine c/ll {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm:{uses='g:grp'}/mm:c/ll", 0); + CHECK_LOG_CTX("The default statement is present on leaf-list with a nonzero min-elements.", "/mm:{uses='g:grp'}/mm:c/ll", 0); +} + +static void +test_augment(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node; + const struct lysc_node_choice *ch; + const struct lysc_node_case *c; + const struct lysc_node_container *cont; + const struct lysc_node_action *rpc; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a;prefix a; typedef atype {type string;}" + "container top {leaf a {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}" + "leaf b {type a:atype;}}", LYS_IN_YANG, &mod)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module c {namespace urn:c;prefix c; import a {prefix a;}" + "augment /a:top { container c {leaf c {type a:atype;}}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d;import a {prefix a;} import c {prefix c;}" + "augment /a:top/c:c { leaf d {type a:atype;} leaf c {type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a"))); + assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "b")); + assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "c")); + assert_non_null(ly_ctx_get_module_implemented(UTEST_LYCTX, "d")); + assert_non_null(node = mod->compiled->data); + assert_string_equal(node->name, "top"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "a"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "c"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "c"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "d"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "c"); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module e {namespace urn:e;prefix e;choice ch {leaf a {type string;}}" + "augment /ch/c {when 1; leaf lc2 {type uint16;}}" + "augment /ch { when 1; leaf b {type int8;} case c {leaf lc1 {type uint8;}}}}", LYS_IN_YANG, &mod)); + assert_non_null((ch = (const struct lysc_node_choice *)mod->compiled->data)); + assert_null(mod->compiled->data->next); + assert_string_equal("ch", ch->name); + + assert_non_null(c = (const struct lysc_node_case *)ch->cases); + assert_string_equal("a", c->name); + assert_null(c->when); + assert_string_equal("a", c->child->name); + + assert_non_null(c = (const struct lysc_node_case *)c->next); + assert_string_equal("b", c->name); + assert_non_null(c->when); + assert_string_equal("b", c->child->name); + + assert_non_null(c = (const struct lysc_node_case *)c->next); + assert_string_equal("c", c->name); + assert_non_null(c->when); + assert_string_equal("lc1", ((const struct lysc_node_case *)c)->child->name); + assert_null(lysc_node_when(((const struct lysc_node_case *)c)->child)); + assert_string_equal("lc2", ((const struct lysc_node_case *)c)->child->next->name); + assert_non_null(lysc_node_when(((const struct lysc_node_case *)c)->child->next)); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;grouping g {leaf a {type string;}}" + "container c;" + "augment /c {uses g;}}", LYS_IN_YANG, &mod)); + assert_non_null(node = lysc_node_child(mod->compiled->data)); + assert_string_equal(node->name, "a"); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule gsub {belongs-to g {prefix g;}" + "augment /c {container sub;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {namespace urn:g;prefix g;include gsub; container c;" + "augment /c/sub {leaf main {type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null(mod->compiled->data); + assert_string_equal("c", mod->compiled->data->name); + assert_non_null(node = ((struct lysc_node_container *)mod->compiled->data)->child); + assert_string_equal("sub", node->name); + assert_non_null(node = ((struct lysc_node_container *)node)->child); + assert_string_equal("main", node->name); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module himp {namespace urn:hi;prefix hi;container top; rpc func;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {namespace urn:h;prefix h;import himp {prefix hi;}container top;" + "augment /hi:top {container p {presence XXX; leaf x {mandatory true;type string;}}}" + "augment /hi:top {list ll {key x;leaf x {type string;}leaf y {mandatory true; type string;}}}" + "augment /hi:top {leaf l {type string; mandatory true; config false;}}" + "augment /top {leaf l {type string; mandatory true;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_non_null(node = ((struct lysc_node_container *)node)->child); + assert_string_equal("l", node->name); + assert_true(node->flags & LYS_MAND_TRUE); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "himp")); + assert_non_null(node = mod->compiled->data); + assert_non_null(node = ((struct lysc_node_container *)node)->child); + assert_string_equal("p", node->name); + assert_non_null(node = node->next); + assert_string_equal("l", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = node->next); + assert_string_equal("ll", node->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module i {namespace urn:i;prefix i;import himp {prefix hi;}" + "augment /hi:func/hi:input {leaf x {type string;}}" + "augment /hi:func/hi:output {leaf y {type string;}}}", LYS_IN_YANG, NULL)); + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "himp")); + assert_non_null(rpc = mod->compiled->rpcs); + assert_null(rpc->next); + assert_non_null(rpc->input.child); + assert_string_equal("x", rpc->input.child->name); + assert_null(rpc->input.child->next); + assert_non_null(rpc->output.child); + assert_string_equal("y", rpc->output.child->name); + assert_null(rpc->output.child->next); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;yang-version 1.1; container root;" + "grouping grp {notification grp-notif;}" + "augment /root {uses grp;}}", LYS_IN_YANG, &mod)); + assert_non_null(cont = (const struct lysc_node_container *)mod->compiled->data); + assert_null(cont->child); + assert_non_null(cont->notifs); + assert_null(cont->notifs->next); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k; prefix k;yang-version 1.1;" + "feature f;" + "container c {if-feature f; leaf a {type string;}}}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l; prefix l; yang-version 1.1;" + "import k {prefix k;}" + "augment /k:c {leaf b {type string;}}" + "leaf c {when \"/k:c/l:b\"; type string;}}", LYS_IN_YANG, NULL)); + /* no xpath warning expected */ + CHECK_LOG_CTX(NULL, NULL, 0); + assert_null(mod->compiled->data); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;yang-version 1.1;" + "feature f;" + "container root{container cont{if-feature f;}}" + "augment /root/cont {if-feature f; leaf l{type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null(cont = (const struct lysc_node_container *)mod->compiled->data); + assert_null(cont->child); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}" + "augment /x/ {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"/x/\" - unexpected end of expression.", "/aa:{augment='/x/'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}" + "augment /x {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Augment target node \"/x\" from module \"aa\" was not found.", "/aa:{augment='/x'}", 0); + + assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; container c {leaf a {type string;}}" + "augment /c {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Duplicate identifier \"/bb:c/a\" of data definition/RPC/action/notification statement.", "/bb:{augment='/c'}/a", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c {leaf a {type string;}}" + "augment /c/a {leaf a {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Augment target node \"/c/a\" from module \"cc\" was not found.", "/cc:{augment='/c/a'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; container c {leaf a {type string;}}" + "augment /c {case b {leaf d {type int8;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid augment of container node which is not allowed to contain case node \"b\".", "/dd:{augment='/c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; import himp {prefix hi;}" + "augment /hi:top {container c {leaf d {mandatory true; type int8;}}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid augment adding mandatory node \"c\" without making it conditional via when statement.", + "/ee:{augment='/hi:top'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; container top;" + "augment ../top {leaf x {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"../top\" - \"/\" expected instead of \"..\".", "/ff:{augment='../top'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; rpc func;" + "augment /func {leaf x {type int8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Augment target node \"/func\" from module \"gg\" was not found.", "/gg:{augment='/func'}", 0); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import himp {prefix hi;}" + "augment /hi:func/input {leaf x {type string;}}" + "augment /hi:func/output {leaf y {type string;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Augment target node \"/hi:func/input\" from module \"hh\" was not found.", "/hh:{augment='/hi:func/input'}", 0); + CHECK_LOG_CTX("Augment target node \"/hi:func/output\" from module \"hh\" was not found.", "/hh:{augment='/hi:func/output'}", 0); +} + +static void +test_deviation(void **state) +{ + struct lys_module *mod; + const struct lysc_node *node; + const struct lysc_node_list *list; + const struct lysc_node_leaflist *llist; + const struct lysc_node_leaf *leaf; + const char *str; + uint8_t dynamic; + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a {namespace urn:a;prefix a;" + "container top {leaf a {type string;} leaf b {type string;} leaf c {type string;}}" + "choice ch {default c; case b {leaf b{type string;}} case a {leaf a{type string;} leaf x {type string;}}" + " case c {leaf c{type string;}}}" + "rpc func1 { input { leaf x {type int8;}} output {leaf y {type int8;}}}" + "rpc func2;}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;import a {prefix a;}" + "deviation /a:top/a:b {deviate not-supported;}" + "deviation /a:ch/a:a/a:x {deviate not-supported;}" + "deviation /a:ch/a:c {deviate not-supported;}" + "deviation /a:ch/a:b {deviate not-supported;}" + "deviation /a:ch/a:a/a:a {deviate not-supported;}" + "deviation /a:ch {deviate replace {default a;}}" + "deviation /a:func1/a:input {deviate not-supported;}" + "deviation /a:func1/a:output {deviate not-supported;}" + "deviation /a:func2 {deviate not-supported;}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a"))); + assert_non_null(node = mod->compiled->data); + assert_string_equal(node->name, "top"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "a"); + assert_non_null(node = node->next); + assert_string_equal(node->name, "c"); + assert_null(node = node->next); + assert_non_null(node = mod->compiled->data->next); + assert_string_equal("ch", node->name); + assert_non_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(((struct lysc_node_choice *)node)->cases); + assert_null(((struct lysc_node_choice *)node)->cases->next); + assert_non_null(mod->compiled->rpcs); + assert_null(mod->compiled->rpcs->next); + assert_null(mod->compiled->rpcs->input.child); + assert_null(mod->compiled->rpcs->output.child); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {namespace urn:c;prefix c; typedef mytype {type string; units kilometers;}" + "leaf c1 {type mytype;} leaf c2 {type mytype; units meters;} leaf c3 {type mytype; units meters;}" + "deviation /c1 {deviate add {units meters;}}" + "deviation /c2 {deviate delete {units meters;}}" + "deviation /c3 {deviate replace {units centimeters;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("c1", node->name); + assert_string_equal("meters", ((struct lysc_node_leaf *)node)->units); + assert_non_null(node = node->next); + assert_string_equal("c2", node->name); + assert_string_equal("kilometers", ((struct lysc_node_leaf *)node)->units); + assert_non_null(node = node->next); + assert_string_equal("c3", node->name); + assert_string_equal("centimeters", ((struct lysc_node_leaf *)node)->units); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module d {namespace urn:d;prefix d; leaf c1 {type string; must 1;}" + "container c2 {presence yes; must 1; must 2;} leaf c3 {type string; must 1; must 3;}" + "deviation /c1 {deviate add {must 3;}}" + "deviation /c2 {deviate delete {must 2;}}" + "deviation /c3 {deviate delete {must 3; must 1;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("c1", node->name); + assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_node_leaf *)node)->musts)); + assert_string_equal("3", ((struct lysc_node_leaf *)node)->musts[1].cond->expr); + assert_non_null(node = node->next); + assert_string_equal("c2", node->name); + assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_node_container *)node)->musts)); + assert_string_equal("1", ((struct lysc_node_container *)node)->musts[0].cond->expr); + assert_non_null(node = node->next); + assert_string_equal("c3", node->name); + assert_null(((struct lysc_node_leaf *)node)->musts); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module e {yang-version 1.1; namespace urn:e;prefix e; typedef mytype {type string; default nothing;}" + "choice a {default aa;leaf aa {type string;} leaf ab {type string;} leaf ac {type string; mandatory true;}}" + "choice b {default ba;leaf ba {type string;} leaf bb {type string;}}" + "leaf c {default hello; type string;}" + "leaf-list d {default hello; default world; type string;}" + "leaf c2 {type mytype;} leaf-list d2 {type mytype;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {yang-version 1.1; namespace urn:f;prefix f;import e {prefix x;}" + "deviation /x:a {deviate delete {default aa;}}" + "deviation /x:b {deviate delete {default ba;}}" + "deviation /x:c {deviate delete {default hello;}}" + "deviation /x:d {deviate delete {default world;}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e"))); + assert_non_null(node = mod->compiled->data); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(node = node->next); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(leaf = (struct lysc_node_leaf *)node->next); + assert_null(leaf->dflt); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(leaf = (struct lysc_node_leaf *)llist->next); + assert_string_equal("nothing", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(5, leaf->dflt->realtype->refcount); /* 3x type reference, 2x default value reference (typedef's default does not reference own type) */ + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("nothing", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module g {yang-version 1.1; namespace urn:g;prefix g;import e {prefix x;}" + "deviation /x:b {deviate add {default x:ba;}}" + "deviation /x:c {deviate add {default bye;}}" + "deviation /x:d {deviate add {default all; default people;}}" + "deviation /x:c2 {deviate add {default hi; must 1;}}" + "deviation /x:d2 {deviate add {default hi; default all;}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e"))); + assert_non_null(node = mod->compiled->data); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(node = node->next); + assert_non_null(((struct lysc_node_choice *)node)->dflt); + assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name); + assert_non_null(leaf = (struct lysc_node_leaf *)node->next); + assert_non_null(leaf->dflt); + assert_string_equal("bye", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(3, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hello", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("people", llist->dflts[2]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[2], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_non_null(leaf = (struct lysc_node_leaf *)llist->next); + assert_non_null(leaf->dflt); + assert_string_equal("hi", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(6, leaf->dflt->realtype->refcount); /* 3x type reference, 3x default value reference + - previous type's default values were replaced by node's default values where d2 now has 2 default values */ + assert_int_equal(1, LY_ARRAY_COUNT(leaf->musts)); + assert_int_equal(1, LY_ARRAY_COUNT(leaf->musts[0].prefixes)); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("hi", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_string_equal("all", llist->dflts[1]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[1], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module h {yang-version 1.1; namespace urn:h;prefix h;import e {prefix x;}" + "deviation /x:b {deviate replace {default x:ba;}}" + "deviation /x:c {deviate replace {default hello;}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "e"))); + assert_non_null(node = mod->compiled->data); + assert_null(((struct lysc_node_choice *)node)->dflt); + assert_non_null(node = node->next); + assert_non_null(((struct lysc_node_choice *)node)->dflt); + assert_string_equal("ba", ((struct lysc_node_choice *)node)->dflt->name); + assert_non_null(leaf = (struct lysc_node_leaf *)node->next); + assert_non_null(leaf->dflt); + assert_string_equal("hello", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module i {namespace urn:i;prefix i;" + "list l1 {key a; leaf a {type string;} leaf b {type string;} leaf c {type string;}}" + "list l2 {key a; unique \"b c\"; unique \"d\"; leaf a {type string;} leaf b {type string;}" + " leaf c {type string;} leaf d {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module j {namespace urn:j;prefix j;import i {prefix i;}" + "augment /i:l1 {leaf j_c {type string;}}" + "deviation /i:l1 {deviate add {unique \"b j:j_c\"; }}" + "deviation /i:l1 {deviate add {unique \"i:c\";}}" + "deviation /i:l2 {deviate delete {unique \"d\"; unique \"b c\";}}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "i"))); + assert_non_null(list = (struct lysc_node_list *)mod->compiled->data); + assert_string_equal("l1", list->name); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques)); + assert_int_equal(2, LY_ARRAY_COUNT(list->uniques[0])); + assert_string_equal("b", list->uniques[0][0]->name); + assert_string_equal("j_c", list->uniques[0][1]->name); + assert_int_equal(1, LY_ARRAY_COUNT(list->uniques[1])); + assert_string_equal("c", list->uniques[1][0]->name); + assert_non_null(list = (struct lysc_node_list *)list->next); + assert_string_equal("l2", list->name); + assert_null(list->uniques); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k; leaf a {type string;}" + "container top {leaf x {type string;} leaf y {type string; config false;}}" + "deviation /a {deviate add {config false; }}" + "deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = node->next); + assert_string_equal("top", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal("x", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = node->next); + assert_string_equal("y", node->name); + assert_true(node->flags & LYS_CONFIG_R); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l; leaf a {config false; type string;}" + "container top {leaf x {type string;}}" + "deviation /a {deviate replace {config true;}}" + "deviation /top {deviate replace {config false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_true(node->flags & LYS_CONFIG_W); + assert_non_null(node = node->next); + assert_string_equal("top", node->name); + assert_true(node->flags & LYS_CONFIG_R); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal("x", node->name); + assert_true(node->flags & LYS_CONFIG_R); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;" + "container a {leaf a {type string;}}" + "container b {leaf b {mandatory true; type string;}}" + "deviation /a/a {deviate add {mandatory true;}}" + "deviation /b/b {deviate replace {mandatory false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE); + assert_true((lysc_node_child(node)->flags & LYS_MAND_MASK) == LYS_MAND_TRUE); + assert_non_null(node = node->next); + assert_string_equal("b", node->name); + assert_false(node->flags & LYS_MAND_MASK); /* just unset on container */ + assert_true((lysc_node_child(node)->flags & LYS_MAND_MASK) == LYS_MAND_FALSE); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module n {yang-version 1.1; namespace urn:n;prefix n;" + "leaf a {default test; type string;}" + "leaf b {mandatory true; type string;}" + "deviation /a {deviate add {mandatory true;} deviate delete {default test;}}" + "deviation /b {deviate add {default test;} deviate replace {mandatory false;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_null(((struct lysc_node_leaf *)node)->dflt); + assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_TRUE); + assert_non_null(node = node->next); + assert_string_equal("b", node->name); + assert_non_null(((struct lysc_node_leaf *)node)->dflt); + assert_true((node->flags & LYS_MAND_MASK) == LYS_MAND_FALSE); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module o {namespace urn:o;prefix o;" + "leaf-list a {type string;}" + "list b {config false;}" + "leaf-list c {min-elements 1; max-elements 10; type string;}" + "list d {min-elements 10; max-elements 100; config false;}" + "deviation /a {deviate add {min-elements 1; max-elements 10;}}" + "deviation /b {deviate add {min-elements 10; max-elements 100;}}" + "deviation /c {deviate replace {min-elements 10; max-elements 100;}}" + "deviation /d {deviate replace {min-elements 1; max-elements 10;}}}", LYS_IN_YANG, &mod)); + assert_non_null(node = mod->compiled->data); + assert_string_equal("a", node->name); + assert_int_equal(1, ((struct lysc_node_leaflist *)node)->min); + assert_int_equal(10, ((struct lysc_node_leaflist *)node)->max); + assert_non_null(node = node->next); + assert_string_equal("b", node->name); + assert_int_equal(10, ((struct lysc_node_list *)node)->min); + assert_int_equal(100, ((struct lysc_node_list *)node)->max); + assert_non_null(node = node->next); + assert_string_equal("c", node->name); + assert_int_equal(10, ((struct lysc_node_leaflist *)node)->min); + assert_int_equal(100, ((struct lysc_node_leaflist *)node)->max); + assert_non_null(node = node->next); + assert_string_equal("d", node->name); + assert_int_equal(1, ((struct lysc_node_list *)node)->min); + assert_int_equal(10, ((struct lysc_node_list *)node)->max); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module p {yang-version 1.1; namespace urn:p;prefix p; typedef mytype {type int8; default 1;}" + "leaf a {type string; default 10;} leaf-list b {type string;}" + "deviation /a {deviate replace {type mytype;}}" + "deviation /b {deviate replace {type mytype;}}}", LYS_IN_YANG, &mod)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("a", leaf->name); + assert_int_equal(LY_TYPE_INT8, leaf->type->basetype); + assert_string_equal("10", leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(10, leaf->dflt->uint8); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_string_equal("b", llist->name); + assert_int_equal(LY_TYPE_INT8, llist->type->basetype); + assert_int_equal(1, LY_ARRAY_COUNT(llist->dflts)); + assert_string_equal("1", llist->dflts[0]->realtype->plugin->print(UTEST_LYCTX, llist->dflts[0], LY_VALUE_SCHEMA, NULL, &dynamic, NULL)); + assert_int_equal(0, dynamic); + assert_int_equal(1, llist->dflts[0]->uint8); + + /* instance-identifiers with NULL canonical are changed to string types with a canonical value equal to the original value */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module q {yang-version 1.1; namespace urn:q;prefix q; import e {prefix e;}" + "leaf q {type instance-identifier; default \"/e:d2[.='a']\";}" + "leaf-list ql {type instance-identifier; default \"/e:d[.='b']\"; default \"/e:d2[.='c']\";}}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module qdev {yang-version 1.1; namespace urn:qdev;prefix qd; import q {prefix q;}" + "deviation /q:q { deviate replace {type string;}}" + "deviation /q:ql { deviate replace {type string;}}}", LYS_IN_YANG, NULL)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_int_equal(LY_TYPE_STRING, leaf->dflt->realtype->basetype); + assert_non_null(leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_CANON, NULL, NULL, NULL)); + assert_string_equal("/e:d2[.='a']", leaf->dflt->_canonical); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_int_equal(2, LY_ARRAY_COUNT(llist->dflts)); + assert_int_equal(LY_TYPE_STRING, llist->dflts[0]->realtype->basetype); + assert_string_equal("/e:d[.='b']", llist->dflts[0]->_canonical); + assert_int_equal(LY_TYPE_STRING, llist->dflts[0]->realtype->basetype); + assert_string_equal("/e:d2[.='c']", llist->dflts[1]->_canonical); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module r {yang-version 1.1; namespace urn:r;prefix r;" + "typedef mytype {type uint8; default 200;}" + "leaf r {type mytype;} leaf-list lr {type mytype;}" + "deviation /r:r {deviate replace {type string;}}" + "deviation /r:lr {deviate replace {type string;}}}", LYS_IN_YANG, &mod)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("r", leaf->name); + assert_null(leaf->dflt); + assert_non_null(llist = (struct lysc_node_leaflist *)leaf->next); + assert_string_equal("lr", llist->name); + assert_null(llist->dflts); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module s {yang-version 1.1; namespace urn:s;prefix s;" + "leaf s {type instance-identifier {require-instance true;} default /s:x;}" + "leaf x {type string;} leaf y {type string;}" + "deviation /s:s {deviate replace {default /s:y;}}}", LYS_IN_YANG, &mod)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("s", leaf->name); + assert_non_null(leaf->dflt); + assert_non_null(str = leaf->dflt->realtype->plugin->print(UTEST_LYCTX, leaf->dflt, LY_VALUE_SCHEMA, mod->parsed, &dynamic, NULL)); + assert_string_equal("/s:y", str); + if (dynamic) { + free((char *)str); + } + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module t {namespace urn:t;prefix t;" + "leaf l {type string;}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module u {namespace urn:u;prefix u;import t {prefix t;}" + "identity ident;" + "deviation /t:l {deviate replace {type identityref {base ident;}}}" + "}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "t"))); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("l", leaf->name); + assert_int_equal(LY_TYPE_IDENT, leaf->type->basetype); + assert_string_equal("ident", ((struct lysc_type_identityref *)leaf->type)->bases[0]->name); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module v {namespace urn:v;prefix v;" + "identity ident; identity ident2 { base ident; }" + "}", LYS_IN_YANG, NULL)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule w-sub { belongs-to w { prefix w; }" + "import v { prefix v_pref; }" + "leaf l { type string; default \"v_pref:ident2\"; }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module w {namespace urn:w;prefix w;" + "include w-sub;" + "}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module x {namespace urn:x;prefix x;" + "import w { prefix w_pref; } import v { prefix v_pref; }" + "deviation /w_pref:l { deviate replace { type identityref { base v_pref:ident; } } }" + "}", LYS_IN_YANG, NULL)); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("l", leaf->name); + assert_int_equal(LY_TYPE_IDENT, leaf->type->basetype); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module y {namespace urn:y;prefix y;" + "leaf l1 {type string;}" + "leaf l2 {type string;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module z {namespace urn:z;prefix z;" + "import y {prefix y;}" + "deviation \"/y:l1\" {deviate replace {type leafref {path \"/l2\";}}}" + "deviation \"/y:l2\" {deviate replace {type leafref {path \"/z:al2\";}}}" + "leaf al2 {type string;}" + "}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "y"))); + assert_non_null(leaf = (struct lysc_node_leaf *)mod->compiled->data); + assert_string_equal("l1", leaf->name); + assert_int_equal(LY_TYPE_LEAFREF, leaf->type->basetype); + leaf = (struct lysc_node_leaf *)leaf->next; + assert_string_equal("l2", leaf->name); + assert_int_equal(LY_TYPE_LEAFREF, leaf->type->basetype); + + /* complex dependencies */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m-base {namespace urn:m-base;prefix mb;" + "container cont {leaf l {type string;} leaf l2 {type string;}}}", LYS_IN_YANG, NULL)); + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module m-base-aug {namespace urn:m-base-aug;prefix mba;" + "import m-base {prefix mb;}" + "augment /mb:cont {leaf l {type string;} leaf l2 {type string;}}" + "container cont2 {leaf l {type string;}}}" + "\n" + "module m-base-aug2 {namespace urn:m-base-aug2;prefix mba2;" + "import m-base {prefix mb;} import m-base-aug {prefix mba;}" + "augment /mb:cont {leaf augl1 {type string;}}" + "augment /mba:cont2 {leaf augl2 {type string;}}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m-dev {namespace urn:m-dev;prefix md;" + "import m-base-aug {prefix mba;} import m-base-aug2 {prefix mba2;}" + "deviation /mba:cont2/mba2:augl2 {deviate not-supported;}}", LYS_IN_YANG, NULL)); + assert_non_null((mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "m-base-aug"))); + node = mod->compiled->data; + assert_string_equal(node->name, "cont2"); + assert_non_null(node = lysc_node_child(node)); + assert_string_equal(node->name, "l"); + assert_null(node->next); + + /* default identity referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a1-imp {namespace urn:a1-imp;prefix a1i;" + "identity id-base;" + "identity id1 {base id-base; status deprecated;}" + "leaf l {type identityref {base \"id-base\";}}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a1 {namespace urn:a1;prefix a1;" + "import a1-imp {prefix a1i;}" + "deviation \"/a1i:l\" {deviate add {default \"a1i:id1\";}}" + "}", LYS_IN_YANG, NULL)); + + /* default instance-identifier referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a2-imp {namespace urn:a2-imp;prefix a2i;" + "leaf l {type instance-identifier;}" + "leaf k {type string; status deprecated;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a2 {namespace urn:a2;prefix a2;" + "import a2-imp {prefix a2i;}" + "deviation \"/a2i:l\" {deviate add {default \"/a2i:k\";}}" + "}", LYS_IN_YANG, NULL)); + + /* must referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a3-imp {namespace urn:a3-imp;prefix a3i;" + "leaf l {type string;}" + "leaf k {type string; status deprecated;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a3 {namespace urn:a3;prefix a3;" + "import a3-imp {prefix a3i;}" + "deviation \"/a3i:l\" {deviate add {must \"string-length(/a3i:k) > 0\";}}" + "}", LYS_IN_YANG, NULL)); + + /* type leafref referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a4-imp {namespace urn:a4-imp;prefix a4i;" + "leaf l {type string;}" + "leaf k {type string; status deprecated;}" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a4 {namespace urn:a4;prefix a4;" + "import a4-imp {prefix a4i;}" + "deviation \"/a4i:l\" {deviate replace {type leafref {path \"/a4i:k\";}}}" + "}", LYS_IN_YANG, NULL)); + + /* unique referencing deprecated */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module a5-imp {namespace urn:a5-imp;prefix a5i;" + "list l1 {key \"k\";" + " leaf k {type string;}" + " leaf l {type string; status deprecated;}" + "}}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a5 {namespace urn:a5;prefix a5;" + "import a5-imp {prefix a5i;}" + "deviation \"/a5i:l1\" {deviate add {unique \"a5i:l\";}}" + "}", LYS_IN_YANG, NULL)); + + assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa1 {namespace urn:aa1;prefix aa1;import a {prefix a;}" + "deviation /a:top/a:z {deviate not-supported;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Deviation(s) target node \"/a:top/a:z\" from module \"aa1\" was not found.", + "/a:{deviation='/a:top/a:z'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa2;import a {prefix a;}" + "deviation /a:top/a:a {deviate not-supported;}" + "deviation /a:top/a:a {deviate add {default error;}}}", LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Multiple deviations of \"/a:top/a:a\" with one of them being \"not-supported\".", + "/aa2:{deviation='/a:top/a:a'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import a {prefix a;}" + "deviation a:top/a:a {deviate not-supported;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"a:top/a:a\" - \"/\" expected instead of \"a:top\".", + "/bb:{deviation='a:top/a:a'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c;" + "deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"units\" property.", + "/cc:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cd {namespace urn:cd;prefix cd; leaf c {type string; units centimeters;}" + "deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"units\" property which already exists (with value \"centimeters\").", + "/cd:{deviation='/c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd1 {namespace urn:dd1;prefix dd1; container c;" + "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to delete \"units\" property.", + "/dd1:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd2 {namespace urn:dd2;prefix dd2; leaf c {type string;}" + "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which is not present.", + "/dd2:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd3 {namespace urn:dd3;prefix dd3; leaf c {type string; units centimeters;}" + "deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which does not match the target's property value \"centimeters\".", + "/dd3:{deviation='/c'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee1 {namespace urn:ee1;prefix ee1; container c;" + "deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"units\" property.", + "/ee1:{deviation='/c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee2 {namespace urn:ee2;prefix ee2; leaf c {type string;}" + "deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation replacing \"units\" property \"meters\" which is not present.", + "/ee2:{deviation='/c'}", 0); + + /* the default is already deleted in /e:a byt module f */ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff1 {namespace urn:ff1;prefix ff1; import e {prefix e;}" + "deviation /e:a {deviate delete {default x:aa;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"x:aa\" which is not present.", + "/ff1:{deviation='/e:a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff3 {namespace urn:ff3;prefix ff3; import e {prefix e;}" + "deviation /e:b {deviate delete {default e:b;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"e:b\" which does not match the target's property value \"x:ba\".", + "/ff3:{deviation='/e:b'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff5 {namespace urn:ff5;prefix ff5; anyxml a;" + "deviation /a {deviate delete {default x;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to delete \"default\" property.", + "/ff5:{deviation='/a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff6 {namespace urn:ff6;prefix ff6; import e {prefix e;}" + "deviation /e:c {deviate delete {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match the target's property value \"hello\".", + "/ff6:{deviation='/e:c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff7 {namespace urn:ff7;prefix ff7; import e {prefix e;}" + "deviation /e:d {deviate delete {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match any of the target's property values.", + "/ff7:{deviation='/e:d'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg1 {namespace urn:gg1;prefix gg1; import e {prefix e;}" + "deviation /e:b {deviate add {default e:a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"x:ba\").", + "/gg1:{deviation='/e:b'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg2 {namespace urn:gg2;prefix gg2; import e {prefix e;}" + "deviation /e:a {deviate add {default x:a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a", 0); + CHECK_LOG_CTX("Default case prefix \"x\" not found in imports of \"gg2\".", "/e:a", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg3 {namespace urn:gg3;prefix gg3; import e {prefix e;}" + "deviation /e:a {deviate add {default a;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a", 0); + CHECK_LOG_CTX("Default case \"a\" not found.", "/e:a", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}" + "deviation /e:c {deviate add {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"hello\").", + "/gg4:{deviation='/e:c'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}" + "deviation /e:a {deviate add {default e:ac;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a", 0); + CHECK_LOG_CTX("Mandatory node \"ac\" under the default case \"e:ac\".", "/e:a", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg5 {namespace urn:gg5;prefix gg5; leaf x {type string; mandatory true;}" + "deviation /x {deviate add {default error;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg5:{deviation='/x'}", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/gg5:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh1 {yang-version 1.1; namespace urn:hh1;prefix hh1; import e {prefix e;}" + "deviation /e:d {deviate replace {default hi;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of leaf-list node - it is not possible to replace \"default\" property.", + "/hh1:{deviation='/e:d'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii1 {namespace urn:ii1;prefix ii1; import i {prefix i;}" + "deviation /i:l1 {deviate delete {unique x;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"x\" which does not match any of the target's property values.", + "/ii1:{deviation='/i:l1'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii2 {namespace urn:ii2;prefix ii2; import i {prefix i;} leaf x { type string;}" + "deviation /i:l2 {deviate delete {unique d;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"d\" which does not match any of the target's property values.", + "/ii2:{deviation='/i:l2'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii3 {namespace urn:ii3;prefix ii3; leaf x { type string;}" + "deviation /x {deviate delete {unique d;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to delete \"unique\" property.", "/ii3:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii4 {namespace urn:ii4;prefix ii4; leaf x { type string;}" + "deviation /x {deviate add {unique d;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to add \"unique\" property.", "/ii4:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj1 {namespace urn:jj1;prefix jj1; choice ch {case a {leaf a{type string;}}}" + "deviation /ch/a {deviate add {config false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to add \"config\" property.", "/jj1:{deviation='/ch/a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj2 {namespace urn:jj2;prefix jj2; container top {config false; leaf x {type string;}}" + "deviation /top/x {deviate add {config true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj2:{deviation='/top/x'}", 0); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/jj2:{deviation='/top/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj4 {namespace urn:jj4;prefix jj4; choice ch {case a {leaf a{type string;}}}" + "deviation /ch/a {deviate replace {config false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to replace \"config\" property.", + "/jj4:{deviation='/ch/a'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj5 {namespace urn:jj5;prefix jj5; container top {leaf x {type string; config true;}}" + "deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj5:top", 0); + CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/jj5:top/x", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj6 {namespace urn:jj6;prefix jj6; leaf x {config false; type string;}" + "deviation /x {deviate add {config true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"config\" property which already exists (with value \"config false\").", + "/jj6:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk1 {namespace urn:kk1;prefix kk1; container top {leaf a{type string;}}" + "deviation /top {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"mandatory\" property.", + "/kk1:{deviation='/top'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk2 {namespace urn:kk2;prefix kk2; container top {leaf a{type string;}}" + "deviation /top {deviate replace {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"mandatory\" property.", + "/kk2:{deviation='/top'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk4 {namespace urn:kk4;prefix kk4; leaf x {mandatory true; type string;}" + "deviation /x {deviate add {mandatory false;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"mandatory\" property which already exists (with value \"mandatory true\").", + "/kk4:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll1 {namespace urn:ll1;prefix ll1; leaf x {default test; type string;}" + "deviation /x {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll1:{deviation='/x'}", 0); + CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "/ll1:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {yang-version 1.1; namespace urn:ll2;prefix ll2; leaf-list x {default test; type string;}" + "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:{deviation='/x'}", 0); + CHECK_LOG_CTX("The default statement is present on leaf-list with a nonzero min-elements.", "/ll2:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {namespace urn:ll2;prefix ll2; choice ch {default a; leaf a {type string;} leaf b {type string;}}" + "deviation /ch {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:ch", 0); + CHECK_LOG_CTX("Invalid mandatory choice with a default case.", "/ll2:ch", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm1 {namespace urn:mm1;prefix mm1; leaf-list x {min-elements 10; type string;}" + "deviation /x {deviate add {max-elements 5;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm1:{deviation='/x'}", 0); + CHECK_LOG_CTX("Leaf-list min-elements 10 is bigger than max-elements 5.", "/mm1:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm2 {namespace urn:mm2;prefix mm2; leaf-list x {max-elements 10; type string;}" + "deviation /x {deviate add {min-elements 20;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm2:{deviation='/x'}", 0); + CHECK_LOG_CTX("Leaf-list min-elements 20 is bigger than max-elements 10.", "/mm2:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm3 {namespace urn:mm3;prefix mm3; list x {min-elements 5; max-elements 10; config false;}" + "deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm3:{deviation='/x'}", 0); + CHECK_LOG_CTX("List min-elements 5 is bigger than max-elements 1.", "/mm3:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm4 {namespace urn:mm4;prefix mm4; list x {min-elements 5; max-elements 10; config false;}" + "deviation /x {deviate replace {min-elements 20;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm4:{deviation='/x'}", 0); + CHECK_LOG_CTX("List min-elements 20 is bigger than max-elements 10.", "/mm4:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm5 {namespace urn:mm5;prefix mm5; leaf-list x {type string; min-elements 5;}" + "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", + "/mm5:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm6 {namespace urn:mm6;prefix mm6; list x {config false; min-elements 5;}" + "deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", + "/mm6:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm7 {namespace urn:mm7;prefix mm7; leaf-list x {type string; max-elements 5;}" + "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", + "/mm7:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm8 {namespace urn:mm8;prefix mm8; list x {config false; max-elements 5;}" + "deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", + "/mm8:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn1 {namespace urn:nn1;prefix nn1; anyxml x;" + "deviation /x {deviate replace {type string;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to replace \"type\" property.", "/nn1:{deviation='/x'}", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn2 {namespace urn:nn2;prefix nn2; leaf-list x {type string;}" + "deviation /x {deviate replace {type empty;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/nn2:{deviation='/x'}", 0); + CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/nn2:{deviation='/x'}", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo1 {namespace urn:oo1;prefix oo1; leaf x {type uint16; default 300;}" + "deviation /x {deviate replace {type uint8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Value \"300\" is out of type uint8 min/max bounds.).", "/oo1:x", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo2 {yang-version 1.1;namespace urn:oo2;prefix oo2; leaf-list x {type uint16; default 10; default 300;}" + "deviation /x {deviate replace {type uint8;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Value \"300\" is out of type uint8 min/max bounds.).", "/oo2:x", 0); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo3 {namespace urn:oo3;prefix oo3; leaf x {type uint8;}" + "deviation /x {deviate add {default 300;}}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Invalid default - value does not fit the type " + "(Value \"300\" is out of type uint8 min/max bounds.).", "/oo3:x", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp; leaf l { type leafref {path /c/x;}}" + "container c {leaf x {type string;} leaf y {type string;}}}", LYS_IN_YANG, &mod)); + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp1 {namespace urn:pp1;prefix pp1; import pp {prefix pp;}" + "deviation /pp:c/pp:x {deviate not-supported;}}", LYS_IN_YANG, &mod)); + CHECK_LOG_CTX("Target of leafref \"l\" cannot be referenced because it is disabled.", "/pp:l", 0); + CHECK_LOG_CTX("Not found node \"x\" in path.", "/pp:l", 0); +} + +static void +test_when(void **state) +{ + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " container cont {\n" + " leaf l {\n" + " when \"/cont/lst[val='25']\";\n" + " type empty;\n" + " }\n" + " list lst {\n" + " key \"k\";\n" + " leaf k {\n" + " type uint8;\n" + " }\n" + " leaf val {\n" + " when /cont2;\n" + " type int32;\n" + " }\n" + " }\n" + " }\n" + " container cont2 {\n" + " presence \"a\";\n" + " when ../cont/l;\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition cyclic dependency on the node \"cont2\".", "/a:cont/lst/val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " container cont {\n" + " leaf l {\n" + " when \"/cont/lst[val='25']\";\n" + " type empty;\n" + " }\n" + " list lst {\n" + " key \"k\";\n" + " leaf k {\n" + " type uint8;\n" + " }\n" + " leaf val {\n" + " when /cont2;\n" + " type int32;\n" + " }\n" + " }\n" + " }\n" + " container cont2 {\n" + " presence \"a\";\n" + " when ../cont/lst/val;\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition cyclic dependency on the node \"cont2\".", "/a:cont/lst/val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " leaf val {\n" + " type int64;\n" + " when \"../val='25'\";\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "/a:val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " grouping grp {\n" + " leaf val {\n" + " type int64;\n" + " }\n" + " }\n" + " uses grp {\n" + " when \"val='25'\";\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "/a:val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " augment /cont {\n" + " when \"val='25'\";\n" + " leaf val {\n" + " type int64;\n" + " }\n" + " }\n" + " container cont;\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node value.", "/a:cont/val", 0); + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, + "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " augment /cont {\n" + " when \"aug-cont/aug-l\";\n" + " container aug-cont {\n" + " leaf aug-l {\n" + " type int64;\n" + " }\n" + " }\n" + " }\n" + " container cont;\n" + "}", + LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("When condition is accessing its own conditional node children.", "/a:cont/aug-cont", 0); + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module b {\n" + " namespace urn:b;\n" + " prefix b;\n" + " container c {\n" + " list l {\n" + " key name;\n" + " leaf name {\n" + " type string;\n" + " }\n" + "\n" + " container cond-data {\n" + " when \"/c/l2[name = current()/../name]/val = 'value'\";\n" + " leaf cond-leaf {\n" + " type string;\n" + " default \"default_val\";\n" + " }\n" + " }\n" + " }\n" + " list l2 {\n" + " key name;\n" + " leaf name {\n" + " type string;\n" + " }\n" + "\n" + " container c2 {\n" + " leaf val {\n" + " type string;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}", + LYS_IN_YANG, NULL)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module c1 {" + " namespace urn:c1;" + " prefix c1;" + " container my-container {" + " leaf my-type {" + " type string;" + " }" + " }" + "}\n" + "module c2 {" + " namespace \"urn:c2\";" + " prefix c2;" + " grouping my-group {" + " leaf my-leaf {" + " type string;" + " }" + " }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module c3 {" + " namespace \"urn:c3\";" + " prefix c3;" + " import c1 {" + " prefix c1;" + " }" + " import c2 {" + " prefix c2;" + " }" + " augment \"/c1:my-container\" {" + " uses c2:my-group {" + " when \"./c1:my-type = '42'\";" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module d1 {" + " namespace urn:d1;" + " prefix d1;" + " container ifm {" + " container interfaces {" + " list interface {" + " key \"name\";" + " leaf name {" + " type string;" + " }" + " container ethernet {" + " container main-interface {" + " container l2-attribute {" + " when \"not(/d1:ifm/d1:interfaces/d1:interface/d1:trunk/d1:members/d1:member[d1:name=current()/../../../d1:name])\";" + " presence \"\";" + " }" + " }" + " }" + " container trunk {" + " container members {" + " list member {" + " key \"name\";" + " leaf name {" + " type string;" + " }" + " }" + " }" + " }" + " }" + " }" + " }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module d2 {" + " namespace \"urn:d2\";" + " prefix d2;" + " import d1 {" + " prefix d1;" + " }" + " augment \"/d1:ifm/d1:interfaces/d1:interface/d1:ethernet/d1:main-interface\" {" + " when \"not(d1:l2-attribute)\";" + " container extra-attribute {" + " presence \"\";" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); +} + +static void +test_must(void **state) +{ + /* "*" must not be restricted to any module */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module a {" + " namespace urn:a;" + " prefix a;" + " container cont {" + " leaf l {" + " type empty;" + " }" + " list lst {" + " key \"k\";" + " leaf k {" + " type uint8;" + " }" + " }" + " }" + "}"); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module a-aug {" + " namespace urn:aa;" + " prefix aa;" + " import a {" + " prefix a;" + " }" + " augment /a:cont {" + " container cont2 {" + " must \"/a:cont/*/a:k\";" + " leaf aug {" + " type empty;" + " }" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); + /* no warnings */ + CHECK_LOG_CTX(NULL, NULL, 0); + + /* must referencing disabled leafref in another module */ + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, + "module b-imp {" + " yang-version 1.1;" + " namespace \"urn:b-imp\";" + " prefix \"bi\";" + "" + " feature feat;" + "" + " grouping band-capabilities {" + " leaf band-number {" + " type uint16;" + " }" + "" + " container sub-band-info {" + " when \"../band-number = '46'\";" + " if-feature \"bi:feat\";" + " leaf number-of-laa-scarriers {" + " type uint8;" + " }" + " }" + " }" + "" + " container module-capability {" + " list band-capabilities {" + " key band-number;" + " config false;" + " uses band-capabilities;" + " }" + " container rw-sub-band-info {" + " if-feature \"bi:feat\";" + " leaf rw-number-of-laa-scarriers {" + " type leafref {" + " path \"/module-capability/band-capabilities/sub-band-info/number-of-laa-scarriers\";" + " require-instance false;" + " }" + " }" + " }" + " }" + "}"); + + ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module b {" + " yang-version 1.1;" + " namespace \"urn:b\";" + " prefix \"b\";" + "" + " import b-imp {" + " prefix \"bi\";" + " }" + "" + " container laa-config {" + " must \"number-of-laa-scarriers <= /bi:module-capability/bi:rw-sub-band-info/bi:rw-number-of-laa-scarriers\";" + " }" + "}", + LYS_IN_YANG, NULL)); + ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED); + + CHECK_LOG_CTX("Schema node \"number-of-laa-scarriers\" not found; in expr \"number-of-laa-scarriers\" " + "with context node \"/b:laa-config\".", NULL, 0); +} + +static void +test_unique_disabled(void **state) +{ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, + "module list-unique {" + " namespace urn:lu;" + " prefix lu;" + " feature f;" + " list l {" + " key \"k\";" + " unique \"v\";" + " leaf k {" + " type string;" + " }" + " leaf v {" + " if-feature f;" + " type string;" + " }" + " }" + " list l2 {" + " key \"k\";" + " unique \"v1 v2\";" + " leaf k {" + " type string;" + " }" + " leaf v1 {" + " if-feature f;" + " type string;" + " }" + " leaf v2 {" + " type string;" + " }" + " }" + "}", + LYS_IN_YANG, NULL)); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_module, setup), + UTEST(test_submodule, setup), + UTEST(test_name_collisions, setup), + UTEST(test_type_length, setup), + UTEST(test_type_range, setup), + UTEST(test_type_pattern, setup), + UTEST(test_type_enum, setup), + UTEST(test_type_dec64, setup), + UTEST(test_type_instanceid, setup), + UTEST(test_identity, setup), + UTEST(test_type_identityref, setup), + UTEST(test_type_leafref, setup), + UTEST(test_type_empty, setup), + UTEST(test_type_union, setup), + UTEST(test_type_dflt, setup), + UTEST(test_type_exts, setup), + UTEST(test_status, setup), + UTEST(test_node_container, setup), + UTEST(test_node_leaflist, setup), + UTEST(test_node_list, setup), + UTEST(test_node_choice, setup), + UTEST(test_node_anydata, setup), + UTEST(test_action, setup), + UTEST(test_notification, setup), + UTEST(test_grouping, setup), + UTEST(test_uses, setup), + UTEST(test_refine, setup), + UTEST(test_augment, setup), + UTEST(test_deviation, setup), + UTEST(test_when, setup), + UTEST(test_must, setup), + UTEST(test_unique_disabled, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_yang.c b/tests/utests/schema/test_yang.c new file mode 100644 index 0000000..67f9747 --- /dev/null +++ b/tests/utests/schema/test_yang.c @@ -0,0 +1,1758 @@ +/** + * @file test_yang.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for YANG module parser and printer + * + * Copyright (c) 2018 - 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 <stdio.h> +#include <string.h> + +#include "in_internal.h" +#include "ly_common.h" +#include "parser_internal.h" +#include "schema_compile.h" +#include "tree_edit.h" +#include "tree_schema.h" +#include "tree_schema_free.h" + +/* originally static functions from parser_yang.c and parser_yin.c */ +LY_ERR buf_add_char(struct ly_ctx *ctx, struct ly_in *in, size_t len, char **buf, size_t *buf_len, size_t *buf_used); +LY_ERR buf_store_char(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, + size_t *word_len, char **word_b, size_t *buf_len, uint8_t need_buf, uint8_t *prefix); +LY_ERR get_keyword(struct lysp_yang_ctx *ctx, enum ly_stmt *kw, char **word_p, size_t *word_len); +LY_ERR get_argument(struct lysp_yang_ctx *ctx, enum yang_arg arg, + uint16_t *flags, char **word_p, char **word_b, size_t *word_len); +LY_ERR skip_comment(struct lysp_yang_ctx *ctx, uint8_t comment); + +LY_ERR parse_action(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_action **actions); +LY_ERR parse_any(struct lysp_yang_ctx *ctx, enum ly_stmt kw, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_augment(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_augment **augments); +LY_ERR parse_case(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_container(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_deviate(struct lysp_yang_ctx *ctx, struct lysp_deviate **deviates); +LY_ERR parse_deviation(struct lysp_yang_ctx *ctx, struct lysp_deviation **deviations); +LY_ERR parse_grouping(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_grp **groupings); +LY_ERR parse_choice(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_leaf(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_leaflist(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_list(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts); +LY_ERR parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts); +LY_ERR parse_module(struct lysp_yang_ctx *ctx, struct lysp_module *mod); +LY_ERR parse_notif(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node_notif **notifs); +LY_ERR parse_submodule(struct lysp_yang_ctx *ctx, struct lysp_submodule *submod); +LY_ERR parse_uses(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_node **siblings); +LY_ERR parse_when(struct lysp_yang_ctx *ctx, struct lysp_when **when_p); +LY_ERR parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct lysp_type_enum *enm); + +struct lysp_yang_ctx *YCTX; +struct lysf_ctx fctx; + +struct ly_in in = {0}; + +#define YCTX_INIT \ + in.line = 1; \ + YCTX->in = ∈ \ + LOG_LOCINIT(UTEST_LYCTX, NULL, NULL, NULL, &in) + +static int +setup(void **state) +{ + struct lysp_module *pmod; + + UTEST_SETUP; + + /* allocate parser context */ + YCTX = calloc(1, sizeof(*YCTX)); + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + YCTX->format = LYS_IN_YANG; + ly_set_new(&YCTX->parsed_mods); + + /* allocate new parsed module */ + pmod = calloc(1, sizeof *pmod); + ly_set_add(YCTX->parsed_mods, pmod, 1, NULL); + + /* allocate new module */ + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->ctx = UTEST_LYCTX; + pmod->mod->parsed = pmod; + + /* initilize and use the global easily available and customizable input handler */ + in.line = 1; + YCTX->in = ∈ + ly_log_location(NULL, NULL, NULL, &in); + + fctx.ctx = PARSER_CTX(YCTX); + fctx.mod = pmod->mod; + + return 0; +} + +static int +teardown(void **state) +{ + lys_module_free(&fctx, PARSER_CUR_PMOD(YCTX)->mod, 0); + ly_log_location_revert(0, 0, 0, 1); + + ly_set_free(YCTX->parsed_mods, NULL); + ly_set_erase(&YCTX->ext_inst, NULL); + free(YCTX); + YCTX = NULL; + + lysf_ctx_erase(&fctx); + + UTEST_TEARDOWN; + + return 0; +} + +#define TEST_DUP_GENERIC(PREFIX, MEMBER, VALUE1, VALUE2, FUNC, RESULT, LINE, CLEANUP) \ + in.current = PREFIX MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, FUNC(YCTX, RESULT)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, LINE);\ + CLEANUP +static void +test_helpers(void **state) +{ + char *buf, *p; + size_t len, size; + uint8_t prefix = 0; + + /* storing into buffer */ + in.current = "abcd"; + buf = NULL; + size = len = 0; + assert_int_equal(LY_SUCCESS, buf_add_char(NULL, &in, 2, &buf, &size, &len)); + assert_int_not_equal(0, size); + assert_int_equal(2, len); + assert_string_equal("cd", in.current); + assert_false(strncmp("ab", buf, 2)); + free(buf); + buf = NULL; + + /* invalid first characters */ + len = 0; + in.current = "2invalid"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + in.current = ".invalid"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + in.current = "-invalid"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + /* invalid following characters */ + len = 3; /* number of characters read before the str content */ + in.current = "!"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + in.current = ":"; + assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + UTEST_LOG_CTX_CLEAN; + /* valid colon for prefixed identifiers */ + len = size = 0; + p = NULL; + prefix = 0; + in.current = "x:id"; + assert_int_equal(LY_SUCCESS, buf_store_char(YCTX, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 0, &prefix)); + assert_int_equal(1, len); + assert_null(buf); + assert_string_equal(":id", in.current); + assert_int_equal('x', p[len - 1]); + assert_int_equal(LY_SUCCESS, buf_store_char(YCTX, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix)); + assert_int_equal(2, len); + assert_string_equal("id", in.current); + assert_int_equal(':', p[len - 1]); + free(buf); + prefix = 0; + + /* checking identifiers */ + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, NULL)); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, '#', 1, NULL)); + CHECK_LOG_CTX("Invalid identifier first character '#' (0x0023).", NULL, 1); + + assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, 'a', 1, &prefix)); + assert_int_equal(0, prefix); + assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix)); + assert_int_equal(1, prefix); + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix)); + CHECK_LOG_CTX("Invalid identifier first character ':' (0x003a).", NULL, 1); + assert_int_equal(1, prefix); + assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, 'b', 0, &prefix)); + assert_int_equal(2, prefix); + /* second colon is invalid */ + assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix)); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); +} + +#define TEST_GET_ARGUMENT_SUCCESS(INPUT_TEXT, CTX, ARG_TYPE, EXPECT_WORD, EXPECT_LEN, EXPECT_CURRENT, EXPECT_LINE)\ + {\ + const char * text = INPUT_TEXT;\ + in.line = 1;\ + in.current = text;\ + assert_int_equal(LY_SUCCESS, get_argument(CTX, Y_MAYBE_STR_ARG, NULL, &word, &buf, &len));\ + assert_string_equal(word, EXPECT_WORD);\ + assert_int_equal(len, EXPECT_LEN);\ + assert_string_equal(EXPECT_CURRENT, in.current);\ + assert_int_equal(EXPECT_LINE, in.line);\ + } + +static void +test_comments(void **state) +{ + char *word, *buf; + size_t len; + + TEST_GET_ARGUMENT_SUCCESS(" // this is a text of / one * line */ comment\nargument;", + YCTX, Y_STR_ARG, "argument;", 8, ";", 2); + assert_null(buf); + + TEST_GET_ARGUMENT_SUCCESS("/* this is a \n * text // of / block * comment */\"arg\" + \"ume\" \n + \n \"nt\";", + YCTX, Y_STR_ARG, "argument", 8, ";", 4); + assert_ptr_equal(buf, word); + free(word); + + in.line = 1; + in.current = " this is one line comment on last line"; + assert_int_equal(LY_SUCCESS, skip_comment(YCTX, 1)); + assert_true(in.current[0] == '\0'); + + in.line = 1; + in.current = " this is a not terminated comment x"; + assert_int_equal(LY_EVALID, skip_comment(YCTX, 2)); + CHECK_LOG_CTX("Unexpected end-of-input, non-terminated comment.", NULL, 1); + assert_true(in.current[0] == '\0'); +} + +static void +test_arg(void **state) +{ + char *word, *buf; + size_t len; + + /* missing argument */ + in.current = ";"; + assert_int_equal(LY_SUCCESS, get_argument(YCTX, Y_MAYBE_STR_ARG, NULL, &word, &buf, &len)); + assert_null(word); + + in.current = "{"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \"{\", expected an argument.", NULL, 1); + + /* invalid escape sequence */ + in.current = "\"\\s\""; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Double-quoted string unknown special character \'\\s\'.", NULL, 1); + + TEST_GET_ARGUMENT_SUCCESS("\'\\s\'", YCTX, Y_STR_ARG, "\\s\'", 2, "", 1); + + /* invalid character after the argument */ + in.current = "hello\""; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \"\"\", expected unquoted string character, optsep, semicolon or opening brace.", NULL, 1); + + in.current = "hello}"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \"}\", expected unquoted string character, optsep, semicolon or opening brace.", NULL, 1); + /* invalid identifier-ref-arg-str */ + in.current = "pre:pre:value"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); + + in.current = "\"\";"; /* empty identifier is not allowed */ + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_IDENTIF_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Statement argument is required.", NULL, 1); + + in.current = "\"\";"; /* empty reference identifier is not allowed */ + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Statement argument is required.", NULL, 1); + + /* slash is not an invalid character */ + TEST_GET_ARGUMENT_SUCCESS("hello/x\t", YCTX, Y_STR_ARG, "hello/x\t", 7, "\t", 1); + assert_null(buf); + + /* different quoting */ + TEST_GET_ARGUMENT_SUCCESS("hello/x\t", YCTX, Y_STR_ARG, "hello/x\t", 7, "\t", 1); + + TEST_GET_ARGUMENT_SUCCESS("hello ", YCTX, Y_STR_ARG, "hello ", 5, " ", 1); + + TEST_GET_ARGUMENT_SUCCESS("hello/*comment*/\n", YCTX, Y_STR_ARG, "hello/*comment*/\n", 5, "\n", 1); + + TEST_GET_ARGUMENT_SUCCESS("\"hello\\n\\t\\\"\\\\\";", YCTX, Y_STR_ARG, "hello\n\t\"\\", 9, ";", 1); + free(buf); + + YCTX->indent = 14; + /* - space and tabs before newline are stripped out + * - space and tabs after newline (indentation) are stripped out + */ + TEST_GET_ARGUMENT_SUCCESS("\"hello \t\n\t\t world!\"", YCTX, Y_STR_ARG, "hello\n world!", 14, "", 2); + free(buf); + +/* In contrast to previous, the backslash-escaped tabs are expanded after trimming, so they are preserved */ + YCTX->indent = 14; + TEST_GET_ARGUMENT_SUCCESS("\"hello \\t\n\t\\t world!\"", YCTX, Y_STR_ARG, "hello \t\n\t world!", 16, "", 2); + assert_ptr_equal(word, buf); + free(buf); + + /* Do not handle whitespaces after backslash-escaped newline as indentation */ + YCTX->indent = 14; + TEST_GET_ARGUMENT_SUCCESS("\"hello\\n\t\t world!\"", YCTX, Y_STR_ARG, "hello\n\t\t world!", 15, "", 1); + assert_ptr_equal(word, buf); + free(buf); + + YCTX->indent = 14; + TEST_GET_ARGUMENT_SUCCESS("\"hello\n \tworld!\"", YCTX, Y_STR_ARG, "hello\nworld!", 12, "", 2); + assert_ptr_equal(word, buf); + free(buf); + + TEST_GET_ARGUMENT_SUCCESS("\'hello\'", YCTX, Y_STR_ARG, "hello'", 5, "", 1); + + TEST_GET_ARGUMENT_SUCCESS("\"hel\" +\t\n\"lo\"", YCTX, Y_STR_ARG, "hello", 5, "", 2); + assert_ptr_equal(word, buf); + free(buf); + + in.line = 1; + in.current = "\"hel\" +\t\nlo"; /* unquoted the second part */ + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Both string parts divided by '+' must be quoted.", NULL, 2); + + TEST_GET_ARGUMENT_SUCCESS("\'he\'\t\n+ \"llo\"", YCTX, Y_STR_ARG, "hello", 5, "", 2); + free(buf); + + TEST_GET_ARGUMENT_SUCCESS(" \t\n\"he\"+\'llo\'", YCTX, Y_STR_ARG, "hello", 5, "", 2); + free(buf); + + /* missing argument */ + in.line = 1; + in.current = ";"; + assert_int_equal(LY_EVALID, get_argument(YCTX, Y_STR_ARG, NULL, &word, &buf, &len)); + CHECK_LOG_CTX("Invalid character sequence \";\", expected an argument.", NULL, 1); +} + +#define TEST_STMS_SUCCESS(INPUT_TEXT, CTX, ACTION, EXPECT_WORD)\ + in.current = INPUT_TEXT;\ + assert_int_equal(LY_SUCCESS, get_keyword(CTX, &kw, &word, &len));\ + assert_int_equal(ACTION, kw);\ + assert_int_equal(strlen(EXPECT_WORD), len);\ + assert_true(0 == strncmp(EXPECT_WORD, word, len)) + +static void +test_stmts(void **state) +{ + const char *p; + enum ly_stmt kw; + char *word; + size_t len; + + in.current = "\n// comment\n\tinput\t{"; + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_INPUT, kw); + assert_int_equal(5, len); + assert_string_equal("input\t{", word); + assert_string_equal("\t{", in.current); + + in.current = "\t /* comment */\t output\n\t{"; + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_OUTPUT, kw); + assert_int_equal(6, len); + assert_string_equal("output\n\t{", word); + assert_string_equal("\n\t{", in.current); + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_SYNTAX_LEFT_BRACE, kw); + assert_int_equal(1, len); + assert_string_equal("{", word); + assert_string_equal("", in.current); + + in.current = "/input { "; /* invalid slash */ + assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len)); + CHECK_LOG_CTX("Invalid identifier first character '/'.", NULL, 4); + + in.current = "not-a-statement-nor-extension { "; /* invalid identifier */ + assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len)); + CHECK_LOG_CTX("Invalid character sequence \"not-a-statement-nor-extension\", expected a keyword.", NULL, 4); + + in.current = "path;"; /* missing sep after the keyword */ + assert_int_equal(LY_EVALID, get_keyword(YCTX, &kw, &word, &len)); + CHECK_LOG_CTX("Invalid character sequence \"path;\", expected a keyword followed by a separator.", NULL, 4); + + TEST_STMS_SUCCESS("action ", YCTX, LY_STMT_ACTION, "action"); + + TEST_STMS_SUCCESS("anydata ", YCTX, LY_STMT_ANYDATA, "anydata"); + TEST_STMS_SUCCESS("anyxml ", YCTX, LY_STMT_ANYXML, "anyxml"); + TEST_STMS_SUCCESS("argument ", YCTX, LY_STMT_ARGUMENT, "argument"); + TEST_STMS_SUCCESS("augment ", YCTX, LY_STMT_AUGMENT, "augment"); + TEST_STMS_SUCCESS("base ", YCTX, LY_STMT_BASE, "base"); + TEST_STMS_SUCCESS("belongs-to ", YCTX, LY_STMT_BELONGS_TO, "belongs-to"); + TEST_STMS_SUCCESS("bit ", YCTX, LY_STMT_BIT, "bit"); + TEST_STMS_SUCCESS("case ", YCTX, LY_STMT_CASE, "case"); + TEST_STMS_SUCCESS("choice ", YCTX, LY_STMT_CHOICE, "choice"); + TEST_STMS_SUCCESS("config ", YCTX, LY_STMT_CONFIG, "config"); + TEST_STMS_SUCCESS("contact ", YCTX, LY_STMT_CONTACT, "contact"); + TEST_STMS_SUCCESS("container ", YCTX, LY_STMT_CONTAINER, "container"); + TEST_STMS_SUCCESS("default ", YCTX, LY_STMT_DEFAULT, "default"); + TEST_STMS_SUCCESS("description ", YCTX, LY_STMT_DESCRIPTION, "description"); + TEST_STMS_SUCCESS("deviate ", YCTX, LY_STMT_DEVIATE, "deviate"); + TEST_STMS_SUCCESS("deviation ", YCTX, LY_STMT_DEVIATION, "deviation"); + TEST_STMS_SUCCESS("enum ", YCTX, LY_STMT_ENUM, "enum"); + TEST_STMS_SUCCESS("error-app-tag ", YCTX, LY_STMT_ERROR_APP_TAG, "error-app-tag"); + TEST_STMS_SUCCESS("error-message ", YCTX, LY_STMT_ERROR_MESSAGE, "error-message"); + TEST_STMS_SUCCESS("extension ", YCTX, LY_STMT_EXTENSION, "extension"); + TEST_STMS_SUCCESS("feature ", YCTX, LY_STMT_FEATURE, "feature"); + TEST_STMS_SUCCESS("fraction-digits ", YCTX, LY_STMT_FRACTION_DIGITS, "fraction-digits"); + TEST_STMS_SUCCESS("grouping ", YCTX, LY_STMT_GROUPING, "grouping"); + TEST_STMS_SUCCESS("identity ", YCTX, LY_STMT_IDENTITY, "identity"); + TEST_STMS_SUCCESS("if-feature ", YCTX, LY_STMT_IF_FEATURE, "if-feature"); + TEST_STMS_SUCCESS("import ", YCTX, LY_STMT_IMPORT, "import"); + TEST_STMS_SUCCESS("include ", YCTX, LY_STMT_INCLUDE, "include"); + TEST_STMS_SUCCESS("input{", YCTX, LY_STMT_INPUT, "input"); + TEST_STMS_SUCCESS("key ", YCTX, LY_STMT_KEY, "key"); + TEST_STMS_SUCCESS("leaf ", YCTX, LY_STMT_LEAF, "leaf"); + TEST_STMS_SUCCESS("leaf-list ", YCTX, LY_STMT_LEAF_LIST, "leaf-list"); + TEST_STMS_SUCCESS("length ", YCTX, LY_STMT_LENGTH, "length"); + TEST_STMS_SUCCESS("list ", YCTX, LY_STMT_LIST, "list"); + TEST_STMS_SUCCESS("mandatory ", YCTX, LY_STMT_MANDATORY, "mandatory"); + TEST_STMS_SUCCESS("max-elements ", YCTX, LY_STMT_MAX_ELEMENTS, "max-elements"); + TEST_STMS_SUCCESS("min-elements ", YCTX, LY_STMT_MIN_ELEMENTS, "min-elements"); + TEST_STMS_SUCCESS("modifier ", YCTX, LY_STMT_MODIFIER, "modifier"); + TEST_STMS_SUCCESS("module ", YCTX, LY_STMT_MODULE, "module"); + TEST_STMS_SUCCESS("must ", YCTX, LY_STMT_MUST, "must"); + TEST_STMS_SUCCESS("namespace ", YCTX, LY_STMT_NAMESPACE, "namespace"); + TEST_STMS_SUCCESS("notification ", YCTX, LY_STMT_NOTIFICATION, "notification"); + TEST_STMS_SUCCESS("ordered-by ", YCTX, LY_STMT_ORDERED_BY, "ordered-by"); + TEST_STMS_SUCCESS("organization ", YCTX, LY_STMT_ORGANIZATION, "organization"); + TEST_STMS_SUCCESS("output ", YCTX, LY_STMT_OUTPUT, "output"); + TEST_STMS_SUCCESS("path ", YCTX, LY_STMT_PATH, "path"); + TEST_STMS_SUCCESS("pattern ", YCTX, LY_STMT_PATTERN, "pattern"); + TEST_STMS_SUCCESS("position ", YCTX, LY_STMT_POSITION, "position"); + TEST_STMS_SUCCESS("prefix ", YCTX, LY_STMT_PREFIX, "prefix"); + TEST_STMS_SUCCESS("presence ", YCTX, LY_STMT_PRESENCE, "presence"); + TEST_STMS_SUCCESS("range ", YCTX, LY_STMT_RANGE, "range"); + TEST_STMS_SUCCESS("reference ", YCTX, LY_STMT_REFERENCE, "reference"); + TEST_STMS_SUCCESS("refine ", YCTX, LY_STMT_REFINE, "refine"); + TEST_STMS_SUCCESS("require-instance ", YCTX, LY_STMT_REQUIRE_INSTANCE, "require-instance"); + TEST_STMS_SUCCESS("revision ", YCTX, LY_STMT_REVISION, "revision"); + TEST_STMS_SUCCESS("revision-date ", YCTX, LY_STMT_REVISION_DATE, "revision-date"); + TEST_STMS_SUCCESS("rpc ", YCTX, LY_STMT_RPC, "rpc"); + TEST_STMS_SUCCESS("status ", YCTX, LY_STMT_STATUS, "status"); + TEST_STMS_SUCCESS("submodule ", YCTX, LY_STMT_SUBMODULE, "submodule"); + TEST_STMS_SUCCESS("type ", YCTX, LY_STMT_TYPE, "type"); + TEST_STMS_SUCCESS("typedef ", YCTX, LY_STMT_TYPEDEF, "typedef"); + TEST_STMS_SUCCESS("unique ", YCTX, LY_STMT_UNIQUE, "unique"); + TEST_STMS_SUCCESS("units ", YCTX, LY_STMT_UNITS, "units"); + TEST_STMS_SUCCESS("uses ", YCTX, LY_STMT_USES, "uses"); + TEST_STMS_SUCCESS("value ", YCTX, LY_STMT_VALUE, "value"); + TEST_STMS_SUCCESS("when ", YCTX, LY_STMT_WHEN, "when"); + TEST_STMS_SUCCESS("yang-version ", YCTX, LY_STMT_YANG_VERSION, "yang-version"); + TEST_STMS_SUCCESS("yin-element ", YCTX, LY_STMT_YIN_ELEMENT, "yin-element"); + TEST_STMS_SUCCESS(";config false;", YCTX, LY_STMT_SYNTAX_SEMICOLON, ";"); + assert_string_equal("config false;", in.current); + TEST_STMS_SUCCESS("{ config false;", YCTX, LY_STMT_SYNTAX_LEFT_BRACE, "{"); + assert_string_equal(" config false;", in.current); + TEST_STMS_SUCCESS("}", YCTX, LY_STMT_SYNTAX_RIGHT_BRACE, "}"); + assert_string_equal("", in.current); + + /* geenric extension */ + in.current = p = "nacm:default-deny-write;"; + assert_int_equal(LY_SUCCESS, get_keyword(YCTX, &kw, &word, &len)); + assert_int_equal(LY_STMT_EXTENSION_INSTANCE, kw); + assert_int_equal(23, len); + assert_ptr_equal(p, word); +} + +static void +test_minmax(void **state) +{ + uint16_t flags = 0; + uint32_t value = 0; + struct lysp_ext_instance *ext = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + in.current = " 1invalid; ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"1invalid\" of \"min-elements\".", NULL, 1); + + flags = value = 0; + in.current = " -1; ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"-1\" of \"min-elements\".", NULL, 1); + + /* implementation limit */ + flags = value = 0; + in.current = " 4294967296; ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Value \"4294967296\" is out of \"min-elements\" bounds.", NULL, 1); + + flags = value = 0; + in.current = " 1 {config true;} ..."; + assert_int_equal(LY_EVALID, parse_minelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"min-elements\".", NULL, 1); + + in.current = " 1invalid; ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"1invalid\" of \"max-elements\".", NULL, 1); + + flags = value = 0; + in.current = " -1; ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid value \"-1\" of \"max-elements\".", NULL, 1); + + /* implementation limit */ + flags = value = 0; + in.current = " 4294967296; ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Value \"4294967296\" is out of \"max-elements\" bounds.", NULL, 1); + + flags = value = 0; + in.current = " 1 {config true;} ..."; + assert_int_equal(LY_EVALID, parse_maxelements(YCTX, &value, &flags, &ext)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"max-elements\".", NULL, 1); +} + +static void +test_valid_module(void **state) +{ + struct lys_module *mod; + char *printed; + 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" + " yang-version 1.1;\n" + " namespace \"urn:module\";\n" + " prefix mod;\n" + "\n" + " import links {\n" + " prefix mod2;\n" + " }\n" + "\n" + " extension ext;\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" + " list list1 {\n" + " key \"a\";\n" + " leaf a {\n" + " type string;\n" + " }\n" + " leaf x {\n" + " type string;\n" + " }\n" + " leaf y {\n" + " type string;\n" + " }\n" + " }\n" + " container ice-cream-shop {\n" + " container employees {\n" + " when \"/list1/x\";\n" + " list employee {\n" + " key \"id\";\n" + " unique \"name\";\n" + " config true;\n" + " min-elements 0 {\n" + " mod:ext;\n" + " }\n" + " max-elements unbounded;\n" + " leaf id {\n" + " type uint64;\n" + " mandatory true;\n" + " }\n" + " leaf name {\n" + " type string;\n" + " }\n" + " leaf age {\n" + " type uint32;\n" + " }\n" + " }\n" + " }\n" + " }\n" + " container random {\n" + " grouping group {\n" + " leaf g1 {\n" + " type percent;\n" + " mandatory false;\n" + " }\n" + " leaf g2 {\n" + " type string;\n" + " }\n" + " }\n" + " choice switch {\n" + " case a {\n" + " leaf aleaf {\n" + " type string;\n" + " default \"aaa\";\n" + " }\n" + " }\n" + " case c {\n" + " leaf cleaf {\n" + " type string;\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" + " }\n" + " uses group;\n" + " uses mod2:rgroup;\n" + " leaf lref {\n" + " type leafref {\n" + " path \"/mod2:one-leaf\";\n" + " }\n" + " }\n" + " leaf iref {\n" + " type identityref {\n" + " base mod2:just-another-identity;\n" + " }\n" + " }\n" + " }\n" + "\n" + " augment \"/random\" {\n" + " leaf aug-leaf {\n" + " type string;\n" + " }\n" + " }\n" + "\n" + " notification notif;\n" + "\n" + " deviation \"/mod:ice-cream-shop/mod:employees/mod:employee/mod:age\" {\n" + " deviate not-supported {\n" + " mod:ext;\n" + " }\n" + " }\n" + " deviation \"/mod:list1\" {\n" + " deviate add {\n" + " mod:ext;\n" + " must \"1\";\n" + " must \"2\";\n" + " unique \"x\";\n" + " unique \"y\";\n" + " config true;\n" + " min-elements 1;\n" + " max-elements 2;\n" + " }\n" + " }\n" + " deviation \"/mod:ice-cream-shop/mod:employees/mod:employee\" {\n" + " deviate delete {\n" + " unique \"name\";\n" + " }\n" + " }\n" + " deviation \"/mod:random/mod:leaflist\" {\n" + " deviate replace {\n" + " type uint32;\n" + " min-elements 10;\n" + " max-elements 15;\n" + " }\n" + " }\n" + "}\n"; + + UTEST_ADD_MODULE(links_yang, LYS_IN_YANG, NULL, NULL); + UTEST_ADD_MODULE(statements_yang, LYS_IN_YANG, NULL, &mod); + lys_print_mem(&printed, mod, LYS_OUT_YANG, 0); + assert_string_equal(printed, statements_yang); + free(printed); +} + +static struct lysp_module * +mod_renew(struct lysp_yang_ctx *ctx) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_module *pmod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + pmod = calloc(1, sizeof *pmod); + ctx->parsed_mods->objs[0] = pmod; + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->parsed = pmod; + pmod->mod->ctx = ly_ctx; + + ctx->in->line = 1; + fctx.mod = pmod->mod; + + return pmod; +} + +static struct lysp_submodule * +submod_renew(struct lysp_yang_ctx *ctx) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_submodule *submod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + submod = calloc(1, sizeof *submod); + ctx->parsed_mods->objs[0] = submod; + submod->mod = calloc(1, sizeof *submod->mod); + lydict_insert(ly_ctx, "name", 0, &submod->mod->name); + submod->mod->parsed = (struct lysp_module *)submod; + submod->mod->ctx = ly_ctx; + + fctx.mod = submod->mod; + + return submod; +} + +static LY_ERR +test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_module(void **state) +{ + struct lysp_module *mod = NULL; + struct lysp_submodule *submod = NULL; + struct lys_module *m; + struct lysp_yang_ctx *ctx_p; + + mod = mod_renew(YCTX); + + /* missing mandatory substatements */ + in.current = " name {}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + assert_string_equal("name", mod->mod->name); + CHECK_LOG_CTX("Missing mandatory keyword \"namespace\" as a child of \"module\".", NULL, 1); + + mod = mod_renew(YCTX); + in.current = " name {namespace urn:name;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + assert_string_equal("urn:name", mod->mod->ns); + CHECK_LOG_CTX("Missing mandatory keyword \"prefix\" as a child of \"module\".", NULL, 1); + mod = mod_renew(YCTX); + + in.current = " name {namespace urn:name;prefix \"n\";}"; + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); + assert_string_equal("n", mod->mod->prefix); + mod = mod_renew(YCTX); + +#define SCHEMA_BEGINNING " name {yang-version 1.1;namespace urn:x;prefix \"x\";" +#define SCHEMA_BEGINNING2 " name {namespace urn:x;prefix \"x\";" +#define TEST_NODE(NODETYPE, INPUT, NAME) \ + in.current = SCHEMA_BEGINNING INPUT; \ + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); \ + assert_non_null(mod->data); \ + assert_int_equal(NODETYPE, mod->data->nodetype); \ + assert_string_equal(NAME, mod->data->name); \ + mod = mod_renew(YCTX); +#define TEST_GENERIC(INPUT, TARGET, TEST) \ + in.current = SCHEMA_BEGINNING INPUT; \ + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); \ + assert_non_null(TARGET); \ + TEST; \ + mod = mod_renew(YCTX); +#define TEST_DUP(MEMBER, VALUE1, VALUE2, LINE) \ + TEST_DUP_GENERIC(SCHEMA_BEGINNING, MEMBER, VALUE1, VALUE2, \ + parse_module, mod, LINE, mod = mod_renew(YCTX)) + + /* duplicated namespace, prefix */ + TEST_DUP("namespace", "y", "z", 1); + TEST_DUP("prefix", "y", "z", 1); + TEST_DUP("contact", "a", "b", 1); + TEST_DUP("description", "a", "b", 1); + TEST_DUP("organization", "a", "b", 1); + TEST_DUP("reference", "a", "b", 1); + + /* not allowed in module (submodule-specific) */ + in.current = SCHEMA_BEGINNING "belongs-to master {prefix m;}}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Invalid keyword \"belongs-to\" as a child of \"module\".", NULL, 1); + mod = mod_renew(YCTX); + + /* anydata */ + TEST_NODE(LYS_ANYDATA, "anydata test;}", "test"); + /* anyxml */ + TEST_NODE(LYS_ANYXML, "anyxml test;}", "test"); + /* augment */ + TEST_GENERIC("augment /somepath;}", mod->augments, + assert_string_equal("/somepath", mod->augments[0].nodeid)); + /* choice */ + TEST_NODE(LYS_CHOICE, "choice test;}", "test"); + /* contact 0..1 */ + TEST_GENERIC("contact \"firstname\" + \n\t\" surname\";}", mod->mod->contact, + assert_string_equal("firstname surname", mod->mod->contact)); + /* container */ + TEST_NODE(LYS_CONTAINER, "container test;}", "test"); + /* description 0..1 */ + TEST_GENERIC("description \'some description\';}", mod->mod->dsc, + assert_string_equal("some description", mod->mod->dsc)); + /* deviation */ + TEST_GENERIC("deviation /somepath {deviate not-supported;}}", mod->deviations, + assert_string_equal("/somepath", mod->deviations[0].nodeid)); + /* extension */ + TEST_GENERIC("extension test;}", mod->extensions, + assert_string_equal("test", mod->extensions[0].name)); + /* feature */ + TEST_GENERIC("feature test;}", mod->features, + assert_string_equal("test", mod->features[0].name)); + /* grouping */ + TEST_GENERIC("grouping grp;}", mod->groupings, + assert_string_equal("grp", mod->groupings[0].name)); + /* identity */ + TEST_GENERIC("identity test;}", mod->identities, + assert_string_equal("test", mod->identities[0].name)); + /* import */ + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "module zzz { namespace urn:zzz; prefix z;}"); + TEST_GENERIC("import zzz {prefix z;}}", mod->imports, + assert_string_equal("zzz", mod->imports[0].name)); + + /* import - prefix collision */ + in.current = SCHEMA_BEGINNING "import zzz {prefix x;}}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Prefix \"x\" already used as module prefix.", NULL, 1); + mod = mod_renew(YCTX); + + in.current = SCHEMA_BEGINNING "import zzz {prefix y;}import zzz {prefix y;}}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Prefix \"y\" already used to import \"zzz\" module.", NULL, 1); + + mod = mod_renew(YCTX); + ly_log_location_revert(0, 0, 0, 1); + + in.current = "module name10 {yang-version 1.1;namespace urn:name10;prefix \"n10\";import zzz {prefix y;}import zzz {prefix z;}}"; + assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX("Single revision of the module \"zzz\" imported twice.", NULL, 0); + + /* include */ + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "module xxx { namespace urn:xxx; prefix x;}"); + in.current = "module" SCHEMA_BEGINNING "include xxx;}"; + assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, 0); + CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL, 0); + CHECK_LOG_CTX("Data model \"xxx\" not found in local searchdirs.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule failed.", NULL, 0); + CHECK_LOG_CTX("Input data contains module in situation when a submodule is expected.", NULL, 0); + + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to wrong-name {prefix w;}}"); + in.current = "module" SCHEMA_BEGINNING "include xxx;}"; + assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EVALID); + CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, 0); + CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL, 0); + UTEST_LOG_CTX_CLEAN; + + ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to name {prefix x;}}"); + TEST_GENERIC("include xxx;}", mod->includes, + assert_string_equal("xxx", mod->includes[0].name)); + + /* leaf */ + TEST_NODE(LYS_LEAF, "leaf test {type string;}}", "test"); + /* leaf-list */ + TEST_NODE(LYS_LEAFLIST, "leaf-list test {type string;}}", "test"); + /* list */ + TEST_NODE(LYS_LIST, "list test {key a;leaf a {type string;}}}", "test"); + /* notification */ + TEST_GENERIC("notification test;}", mod->notifs, + assert_string_equal("test", mod->notifs[0].name)); + /* organization 0..1 */ + TEST_GENERIC("organization \"CESNET a.l.e.\";}", mod->mod->org, + assert_string_equal("CESNET a.l.e.", mod->mod->org)); + /* reference 0..1 */ + TEST_GENERIC("reference RFC7950;}", mod->mod->ref, + assert_string_equal("RFC7950", mod->mod->ref)); + /* revision */ + TEST_GENERIC("revision 2018-10-12;}", mod->revs, + assert_string_equal("2018-10-12", mod->revs[0].date)); + /* rpc */ + TEST_GENERIC("rpc test;}", mod->rpcs, + assert_string_equal("test", mod->rpcs[0].name)); + /* typedef */ + TEST_GENERIC("typedef test{type string;}}", mod->typedefs, + assert_string_equal("test", mod->typedefs[0].name)); + /* uses */ + TEST_NODE(LYS_USES, "uses test;}", "test"); + /* yang-version */ + in.current = SCHEMA_BEGINNING2 "\n\tyang-version 10;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Invalid value \"10\" of \"yang-version\".", NULL, 0); + mod = mod_renew(YCTX); + in.current = SCHEMA_BEGINNING2 "yang-version 1;yang-version 1.1;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Duplicate keyword \"yang-version\".", NULL, 0); + mod = mod_renew(YCTX); + in.current = SCHEMA_BEGINNING2 "yang-version 1;}"; + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); + assert_int_equal(1, mod->version); + mod = mod_renew(YCTX); + in.current = SCHEMA_BEGINNING2 "yang-version \"1.1\";}"; + assert_int_equal(LY_SUCCESS, parse_module(YCTX, mod)); + assert_int_equal(2, mod->version); + mod = mod_renew(YCTX); + + in.current = "module " SCHEMA_BEGINNING "} module q {namespace urn:q;prefixq;}"; + m = calloc(1, sizeof *m); + m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx; + assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m)); + CHECK_LOG_CTX("Trailing garbage \"module q {names...\" after module, expected end-of-input.", NULL, 1); + lysp_yang_ctx_free(ctx_p); + lys_module_free(&fctx, m, 0); + + in.current = "prefix " SCHEMA_BEGINNING "}"; + m = calloc(1, sizeof *m); + m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx; + assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m)); + CHECK_LOG_CTX("Invalid keyword \"prefix\", expected \"module\" or \"submodule\".", NULL, 1); + lysp_yang_ctx_free(ctx_p); + lys_module_free(&fctx, m, 0); + + in.current = "module " SCHEMA_BEGINNING "leaf enum {type enumeration {enum seven { position 7;}}}}"; + m = calloc(1, sizeof *m); + m->ctx = PARSER_CUR_PMOD(YCTX)->mod->ctx; + assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m)); + CHECK_LOG_CTX("Invalid keyword \"position\" as a child of \"enum\".", NULL, 1); + lysp_yang_ctx_free(ctx_p); + lys_module_free(&fctx, m, 0); + + /* extensions */ + TEST_GENERIC("prefix:test;}", mod->exts, + assert_string_equal("prefix:test", mod->exts[0].name); + assert_int_equal(LY_STMT_MODULE, mod->exts[0].parent_stmt)); + mod = mod_renew(YCTX); + + /* invalid substatement */ + in.current = SCHEMA_BEGINNING "must false;}"; + assert_int_equal(LY_EVALID, parse_module(YCTX, mod)); + CHECK_LOG_CTX("Invalid keyword \"must\" as a child of \"module\".", NULL, 0); + + /* submodule */ + submod = submod_renew(YCTX); + + /* missing mandatory substatements */ + in.current = " subname {}"; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Missing mandatory keyword \"belongs-to\" as a child of \"submodule\".", NULL, 0); + assert_string_equal("subname", submod->name); + + submod = submod_renew(YCTX); + + in.current = " subname {belongs-to name {prefix x;}}"; + assert_int_equal(LY_SUCCESS, parse_submodule(YCTX, submod)); + assert_string_equal("name", submod->mod->name); + submod = submod_renew(YCTX); + +#undef SCHEMA_BEGINNING +#define SCHEMA_BEGINNING " subname {belongs-to name {prefix x;}" + + /* duplicated namespace, prefix */ + in.current = " subname {belongs-to name {prefix x;}belongs-to module1;belongs-to module2;} ..."; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Duplicate keyword \"belongs-to\".", NULL, 0); + submod = submod_renew(YCTX); + + /* not allowed in submodule (module-specific) */ + in.current = SCHEMA_BEGINNING "namespace \"urn:z\";}"; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Invalid keyword \"namespace\" as a child of \"submodule\".", NULL, 0); + submod = submod_renew(YCTX); + in.current = SCHEMA_BEGINNING "prefix m;}}"; + assert_int_equal(LY_EVALID, parse_submodule(YCTX, submod)); + CHECK_LOG_CTX("Invalid keyword \"prefix\" as a child of \"submodule\".", NULL, 0); + submod = submod_renew(YCTX); + + in.current = "submodule " SCHEMA_BEGINNING "} module q {namespace urn:q;prefixq;}"; + assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx_p, PARSER_CUR_PMOD(YCTX)->mod->ctx, (struct lysp_ctx *)YCTX, YCTX->in, &submod)); + CHECK_LOG_CTX("Trailing garbage \"module q {names...\" after submodule, expected end-of-input.", NULL, 1); + lysp_yang_ctx_free(ctx_p); + + in.current = "prefix " SCHEMA_BEGINNING "}"; + assert_int_equal(LY_EVALID, yang_parse_submodule(&ctx_p, PARSER_CUR_PMOD(YCTX)->mod->ctx, (struct lysp_ctx *)YCTX, YCTX->in, &submod)); + CHECK_LOG_CTX("Invalid keyword \"prefix\", expected \"module\" or \"submodule\".", NULL, 1); + lysp_yang_ctx_free(ctx_p); + submod = submod_renew(YCTX); + +#undef TEST_GENERIC +#undef TEST_NODE +#undef TEST_DUP +#undef SCHEMA_BEGINNING +} + +static void +test_deviation(void **state) +{ + struct lysp_deviation *d = NULL; + + /* invalid cardinality */ + TEST_DUP_GENERIC(" test {deviate not-supported;", "description", "a", "b", parse_deviation, &d, 1, ); + TEST_DUP_GENERIC(" test {deviate not-supported;", "reference", "a", "b", parse_deviation, &d, 1, ); + + /* missing mandatory substatement */ + in.current = " test {description text;}"; + assert_int_equal(LY_EVALID, parse_deviation(YCTX, &d)); + CHECK_LOG_CTX("Missing mandatory keyword \"deviate\" as a child of \"deviation\".", NULL, 1); + + /* invalid substatement */ + in.current = " test {deviate not-supported; status obsolete;}"; + assert_int_equal(LY_EVALID, parse_deviation(YCTX, &d)); + CHECK_LOG_CTX("Invalid keyword \"status\" as a child of \"deviation\".", NULL, 1); +} + +static void +test_deviate(void **state) +{ + struct lysp_deviate *d = NULL; + + /* invalid cardinality */ + TEST_DUP_GENERIC("add {", "config", "true", "false", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "mandatory", "true", "false", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "max-elements", "1", "2", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "min-elements", "1", "2", parse_deviate, &d, 1, ); + TEST_DUP_GENERIC("add {", "units", "kilometers", "miles", parse_deviate, &d, 1, ); + + /* invalid substatements */ +#define TEST_NOT_SUP(DEV, STMT, VALUE) \ + in.current = " "DEV" {"STMT" "VALUE";}..."; \ + assert_int_equal(LY_EVALID, parse_deviate(YCTX, &d)); \ + CHECK_LOG_CTX("Deviate \""DEV"\" does not support keyword \""STMT"\".", NULL, 1); + + TEST_NOT_SUP("not-supported", "units", "meters"); + TEST_NOT_SUP("not-supported", "must", "1"); + TEST_NOT_SUP("not-supported", "unique", "x"); + TEST_NOT_SUP("not-supported", "default", "a"); + TEST_NOT_SUP("not-supported", "config", "true"); + TEST_NOT_SUP("not-supported", "mandatory", "true"); + TEST_NOT_SUP("not-supported", "min-elements", "1"); + TEST_NOT_SUP("not-supported", "max-elements", "2"); + TEST_NOT_SUP("not-supported", "type", "string"); + TEST_NOT_SUP("add", "type", "string"); + TEST_NOT_SUP("delete", "config", "true"); + TEST_NOT_SUP("delete", "mandatory", "true"); + TEST_NOT_SUP("delete", "min-elements", "1"); + TEST_NOT_SUP("delete", "max-elements", "2"); + TEST_NOT_SUP("delete", "type", "string"); + TEST_NOT_SUP("replace", "must", "1"); + TEST_NOT_SUP("replace", "unique", "a"); + + in.current = " nonsence; ..."; + assert_int_equal(LY_EVALID, parse_deviate(YCTX, &d)); + CHECK_LOG_CTX("Invalid value \"nonsence\" of \"deviate\".", NULL, 1);\ + assert_null(d); +#undef TEST_NOT_SUP +} + +static void +test_container(void **state) +{ + struct lysp_node_container *c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "cont {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node**)&c)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)c); c = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("presence", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "cont {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; leaf l {type string;}" + "leaf-list ll {type string;} list li;must 'expr';notification not; presence true; reference test;status current;typedef t {type int8;}uses g;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LYSP_NODE(c, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "cont", 0, LYS_CONTAINER, 0, "test", 1); + assert_non_null(c->actions); + assert_non_null(c->child); + assert_non_null(c->groupings); + assert_non_null(c->musts); + assert_non_null(c->notifs); + assert_string_equal("true", c->presence); + assert_non_null(c->typedefs); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; + + /* invalid */ + in.current = " cont {augment /root;} ..."; + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LOG_CTX("Invalid keyword \"augment\" as a child of \"container\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; + in.current = " cont {nonsence true;} ..."; + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LOG_CTX("Invalid character sequence \"nonsence\", expected a keyword.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */ + in.current = " cont {action x;} ..."; + assert_int_equal(LY_EVALID, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"container\" - " + "the statement is allowed only in YANG 1.1 modules.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)c); c = NULL; +} + +static void +test_leaf(void **state) +{ + struct lysp_node_leaf *l = NULL; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node**)&l)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)l); l = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("default", "x", "y"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("mandatory", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("type", "int8", "uint8"); + TEST_DUP("units", "text1", "text2"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content - without mandatory which is mutual exclusive with default */ + in.current = "l {config false;default \"xxx\";description test;if-feature f;" + "must 'expr';reference test;status current;type string; units yyy;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LYSP_NODE(l, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR, 1, "l", 0, LYS_LEAF, 0, "test", 1); + assert_string_equal("xxx", l->dflt.str); + assert_string_equal("yyy", l->units); + assert_string_equal("string", l->type.name); + assert_non_null(l->musts); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + /* full content - now with mandatory */ + in.current = "l {mandatory true; type string;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LYSP_NODE(l, NULL, 0, LYS_MAND_TRUE, 0, "l", 0, LYS_LEAF, 0, NULL, 0); + assert_string_equal("string", l->type.name); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + /* invalid */ + in.current = " l {description \"missing type\";} ..."; + assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"leaf\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + in.current = "l { type iid { path qpud wrong {"; + assert_int_equal(LY_EVALID, parse_leaf(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LOG_CTX("Invalid character sequence \"wrong\", expected a keyword.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; +} + +static void +test_leaflist(void **state) +{ + struct lysp_node_leaflist *ll = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "ll {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node**)&ll)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)ll); ll = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("max-elements", "10", "20"); + TEST_DUP("min-elements", "10", "20"); + TEST_DUP("ordered-by", "user", "system"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("type", "int8", "uint8"); + TEST_DUP("units", "text1", "text2"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content - without min-elements which is mutual exclusive with default */ + in.current = "ll {config false;default \"xxx\"; default \"yyy\";description test;if-feature f;" + "max-elements 10;must 'expr';ordered-by user;reference test;" + "status current;type string; units zzz;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LYSP_NODE(ll, "test", 1, 0x446, 1, "ll", 0, LYS_LEAFLIST, 0, "test", 1); + assert_non_null(ll->dflts); + assert_int_equal(2, LY_ARRAY_COUNT(ll->dflts)); + assert_string_equal("xxx", ll->dflts[0].str); + assert_string_equal("yyy", ll->dflts[1].str); + assert_string_equal("zzz", ll->units); + assert_int_equal(10, ll->max); + assert_int_equal(0, ll->min); + assert_string_equal("string", ll->type.name); + assert_non_null(ll->musts); + assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_MAX, ll->flags); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; + + /* full content - now with min-elements */ + in.current = "ll {min-elements 10; type string;} ..."; + assert_int_equal(LY_SUCCESS, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LYSP_NODE(ll, NULL, 0, 0x200, 0, "ll", 0, LYS_LEAFLIST, 0, NULL, 0); + assert_string_equal("string", ll->type.name); + assert_int_equal(0, ll->max); + assert_int_equal(10, ll->min); + assert_int_equal(LYS_SET_MIN, ll->flags); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; + + /* invalid */ + in.current = " ll {description \"missing type\";} ..."; + assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LOG_CTX("Missing mandatory keyword \"type\" as a child of \"leaf-list\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 - default statement is not allowed */ + in.current = " ll {default xx; type string;} ..."; + assert_int_equal(LY_EVALID, parse_leaflist(YCTX, NULL, (struct lysp_node **)&ll)); + CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"leaf-list\" - the statement is allowed only in YANG 1.1 modules.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)ll); ll = NULL; +} + +static void +test_list(void **state) +{ + struct lysp_node_list *l = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_list(YCTX, NULL, (struct lysp_node**)&l)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)l); l = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("key", "one", "two"); + TEST_DUP("max-elements", "10", "20"); + TEST_DUP("min-elements", "10", "20"); + TEST_DUP("ordered-by", "user", "system"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "l {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; key l; leaf l {type string;}" + "leaf-list ll {type string;} list li;max-elements 10; min-elements 1;must 'expr';notification not; ordered-by system; reference test;" + "status current;typedef t {type int8;}unique xxx;unique yyy;uses g;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_list(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LYSP_NODE(l, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM | LYS_SET_MAX | LYS_SET_MIN, 1, "l", + 0, LYS_LIST, 0, "test", 1); + assert_string_equal("l", l->key); + assert_non_null(l->uniques); + assert_int_equal(2, LY_ARRAY_COUNT(l->uniques)); + assert_string_equal("xxx", l->uniques[0].str); + assert_string_equal("yyy", l->uniques[1].str); + assert_int_equal(10, l->max); + assert_int_equal(1, l->min); + assert_non_null(l->musts); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; + + /* invalid content */ + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */ + in.current = "l {action x;} ..."; + assert_int_equal(LY_EVALID, parse_list(YCTX, NULL, (struct lysp_node **)&l)); + CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"list\" - the statement is allowed only in YANG 1.1 modules.", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)l); l = NULL; +} + +static void +test_choice(void **state) +{ + struct lysp_node_choice *ch = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "ch {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_choice(YCTX, NULL, (struct lysp_node**)&ch)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)ch); ch = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("default", "a", "b"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("mandatory", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content - without default due to a collision with mandatory */ + in.current = "ch {anydata any;anyxml anyxml; case c;choice ch;config false;container c;description test;if-feature f;leaf l {type string;}" + "leaf-list ll {type string;} list li;mandatory true;reference test;status current;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_choice(YCTX, NULL, (struct lysp_node **)&ch)); + CHECK_LYSP_NODE(ch, "test", 1, LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "ch", 0, LYS_CHOICE, 0, "test", 1); + lysp_node_free(&fctx, (struct lysp_node *)ch); ch = NULL; + + /* full content - the default missing from the previous node */ + in.current = "ch {default c;case c;} ..."; + assert_int_equal(LY_SUCCESS, parse_choice(YCTX, NULL, (struct lysp_node **)&ch)); + CHECK_LYSP_NODE(ch, NULL, 0, 0, 0, "ch", 0, LYS_CHOICE, 0, NULL, 0); + assert_string_equal("c", ch->dflt.str); + lysp_node_free(&fctx, (struct lysp_node *)ch); ch = NULL; +} + +static void +test_case(void **state) +{ + struct lysp_node_case *cs = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "cs {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_case(YCTX, NULL, (struct lysp_node**)&cs)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)cs); cs = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "cs {anydata any;anyxml anyxml; choice ch;container c;description test;if-feature f;leaf l {type string;}" + "leaf-list ll {type string;} list li;reference test;status current;uses grp;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_case(YCTX, NULL, (struct lysp_node **)&cs)); + CHECK_LYSP_NODE(cs, "test", 1, LYS_STATUS_CURR, 1, "cs", 0, LYS_CASE, 0, "test", 1); + lysp_node_free(&fctx, (struct lysp_node *)cs); cs = NULL; + + /* invalid content */ + in.current = "cs {config true} ..."; + assert_int_equal(LY_EVALID, parse_case(YCTX, NULL, (struct lysp_node **)&cs)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"case\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)cs); cs = NULL; +} + +static void +test_any(void **state, enum ly_stmt kw) +{ + struct lysp_node_anydata *any = NULL; + + if (kw == LY_STMT_ANYDATA) { + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + } else { + PARSER_CUR_PMOD(YCTX)->version = 1; /* simulate YANG 1.0 */ + } + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_any(YCTX, kw, NULL, (struct lysp_node**)&any)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)any); any = NULL; + + TEST_DUP("config", "true", "false"); + TEST_DUP("description", "text1", "text2"); + TEST_DUP("mandatory", "true", "false"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "any {config true;description test;if-feature f;mandatory true;must 'expr';reference test;status current;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_any(YCTX, kw, NULL, (struct lysp_node **)&any)); + // CHECK_LYSP_NODE(NODE, DSC, EXTS, FLAGS, IFFEATURES, NAME, NEXT, TYPE, PARENT, REF, WHEN) + uint16_t node_type = kw == LY_STMT_ANYDATA ? LYS_ANYDATA : LYS_ANYXML; + + CHECK_LYSP_NODE(any, "test", 1, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, 1, "any", 0, node_type, 0, "test", 1); + assert_non_null(any->musts); + lysp_node_free(&fctx, (struct lysp_node *)any); any = NULL; +} + +static void +test_anydata(void **state) +{ + test_any(state, LY_STMT_ANYDATA); +} + +static void +test_anyxml(void **state) +{ + test_any(state, LY_STMT_ANYXML); +} + +static void +test_grouping(void **state) +{ + struct lysp_node_grp *grp = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, &grp->node); grp = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); +#undef TEST_DUP + + /* full content */ + in.current = "grp {action x;anydata any;anyxml anyxml; choice ch;container c;description test;grouping g;leaf l {type string;}" + "leaf-list ll {type string;} list li;notification not;reference test;status current;typedef t {type int8;}uses g;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_grouping(YCTX, NULL, &grp)); + assert_non_null(grp); + assert_int_equal(LYS_GROUPING, grp->nodetype); + assert_string_equal("grp", grp->name); + assert_string_equal("test", grp->dsc); + assert_non_null(grp->exts); + assert_string_equal("test", grp->ref); + assert_null(grp->parent); + assert_int_equal(LYS_STATUS_CURR, grp->flags); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, &grp->node); + grp = NULL; + + /* invalid content */ + in.current = "grp {config true} ..."; + assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"grouping\".", NULL, 1); + lysp_node_free(&fctx, &grp->node); + grp = NULL; + + in.current = "grp {must 'expr'} ..."; + assert_int_equal(LY_EVALID, parse_grouping(YCTX, NULL, &grp)); + CHECK_LOG_CTX("Invalid keyword \"must\" as a child of \"grouping\".", NULL, 1); + lysp_node_free(&fctx, &grp->node); + grp = NULL; +} + +static void +test_action(void **state) +{ + struct lysp_node_action *rpcs = NULL; + struct lysp_node_container *c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_action(YCTX, NULL, &rpcs)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)rpcs); rpcs = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("input", "{leaf l1 {type empty;}} description a", "{leaf l2 {type empty;}} description a"); + TEST_DUP("output", "{leaf l1 {type empty;}} description a", "{leaf l2 {type empty;}} description a"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); +#undef TEST_DUP + + /* full content */ + in.current = "top;"; + assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + in.current = "func {description test;grouping grp;if-feature f;reference test;status current;typedef mytype {type int8;} m:ext;" + "input {anydata a1; anyxml a2; choice ch; container c; grouping grp; leaf l {type int8;} leaf-list ll {type int8;}" + " list li; must 1; typedef mytypei {type int8;} uses grp; m:ext;}" + "output {anydata a1; anyxml a2; choice ch; container c; grouping grp; leaf l {type int8;} leaf-list ll {type int8;}" + " list li; must 1; typedef mytypeo {type int8;} uses grp; m:ext;}} ..."; + assert_int_equal(LY_SUCCESS, parse_action(YCTX, (struct lysp_node *)c, &rpcs)); + assert_non_null(rpcs); + assert_int_equal(LYS_ACTION, rpcs->nodetype); + assert_string_equal("func", rpcs->name); + assert_string_equal("test", rpcs->dsc); + assert_non_null(rpcs->exts); + assert_non_null(rpcs->iffeatures); + assert_string_equal("test", rpcs->ref); + assert_non_null(rpcs->groupings); + assert_non_null(rpcs->typedefs); + assert_int_equal(LYS_STATUS_CURR, rpcs->flags); + /* input */ + assert_int_equal(rpcs->input.nodetype, LYS_INPUT); + assert_non_null(rpcs->input.groupings); + assert_non_null(rpcs->input.exts); + assert_non_null(rpcs->input.musts); + assert_non_null(rpcs->input.typedefs); + assert_non_null(rpcs->input.child); + /* output */ + assert_int_equal(rpcs->output.nodetype, LYS_OUTPUT); + assert_non_null(rpcs->output.groupings); + assert_non_null(rpcs->output.exts); + assert_non_null(rpcs->output.musts); + assert_non_null(rpcs->output.typedefs); + assert_non_null(rpcs->output.child); + + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)rpcs); rpcs = NULL; + + /* invalid content */ + in.current = "func {config true} ..."; + assert_int_equal(LY_EVALID, parse_action(YCTX, NULL, &rpcs)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"rpc\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)rpcs); rpcs = NULL; + + lysp_node_free(&fctx, (struct lysp_node *)c); +} + +static void +test_notification(void **state) +{ + struct lysp_node_notif *notifs = NULL; + struct lysp_node_container *c = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "func {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_notif(YCTX, NULL, ¬ifs)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)notifs); notifs = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); +#undef TEST_DUP + + /* full content */ + in.current = "top;"; + assert_int_equal(LY_SUCCESS, parse_container(YCTX, NULL, (struct lysp_node **)&c)); + in.current = "ntf {anydata a1; anyxml a2; choice ch; container c; description test; grouping grp; if-feature f; leaf l {type int8;}" + "leaf-list ll {type int8;} list li; must 1; reference test; status current; typedef mytype {type int8;} uses grp; m:ext;}"; + assert_int_equal(LY_SUCCESS, parse_notif(YCTX, (struct lysp_node *)c, ¬ifs)); + assert_non_null(notifs); + assert_int_equal(LYS_NOTIF, notifs->nodetype); + assert_string_equal("ntf", notifs->name); + assert_string_equal("test", notifs->dsc); + assert_non_null(notifs->exts); + assert_non_null(notifs->iffeatures); + assert_string_equal("test", notifs->ref); + assert_non_null(notifs->groupings); + assert_non_null(notifs->typedefs); + assert_non_null(notifs->musts); + assert_non_null(notifs->child); + assert_int_equal(LYS_STATUS_CURR, notifs->flags); + + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + ly_set_erase(&YCTX->grps_nodes, NULL); + lysp_node_free(&fctx, (struct lysp_node *)notifs); notifs = NULL; + + /* invalid content */ + in.current = "ntf {config true} ..."; + assert_int_equal(LY_EVALID, parse_notif(YCTX, NULL, ¬ifs)); + CHECK_LOG_CTX("Invalid keyword \"config\" as a child of \"notification\".", NULL, 1); + lysp_node_free(&fctx, (struct lysp_node *)notifs); notifs = NULL; + + lysp_node_free(&fctx, (struct lysp_node *)c); +} + +static void +test_uses(void **state) +{ + struct lysp_node_uses *u = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_uses(YCTX, NULL, (struct lysp_node**)&u)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node*)u); u = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "grpref {augment some/node;description test;if-feature f;reference test;refine some/other/node;status current;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_uses(YCTX, NULL, (struct lysp_node **)&u)); + CHECK_LYSP_NODE(u, "test", 1, LYS_STATUS_CURR, 1, "grpref", 0, LYS_USES, 0, "test", 1); + assert_non_null(u->augments); + assert_non_null(u->refines); + lysp_node_free(&fctx, (struct lysp_node *)u); u = NULL; +} + +static void +test_augment(void **state) +{ + struct lysp_node_augment *a = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + /* invalid cardinality */ +#define TEST_DUP(MEMBER, VALUE1, VALUE2) \ + in.current = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \ + assert_int_equal(LY_EVALID, parse_augment(YCTX, NULL, &a)); \ + CHECK_LOG_CTX("Duplicate keyword \""MEMBER"\".", NULL, 1); \ + lysp_node_free(&fctx, (struct lysp_node *)a); a = NULL; + + TEST_DUP("description", "text1", "text2"); + TEST_DUP("reference", "1", "2"); + TEST_DUP("status", "current", "obsolete"); + TEST_DUP("when", "true", "false"); +#undef TEST_DUP + + /* full content */ + in.current = "/target/nodeid {action x; anydata any;anyxml anyxml; case cs; choice ch;container c;description test;if-feature f;leaf l {type string;}" + "leaf-list ll {type string;} list li;notification not;reference test;status current;uses g;when true;m:ext;} ..."; + assert_int_equal(LY_SUCCESS, parse_augment(YCTX, NULL, &a)); + assert_non_null(a); + assert_int_equal(LYS_AUGMENT, a->nodetype); + assert_string_equal("/target/nodeid", a->nodeid); + assert_string_equal("test", a->dsc); + assert_non_null(a->exts); + assert_non_null(a->iffeatures); + assert_string_equal("test", a->ref); + assert_non_null(a->when); + assert_null(a->parent); + assert_int_equal(LYS_STATUS_CURR, a->flags); + lysp_node_free(&fctx, (struct lysp_node *)a); a = NULL; +} + +static void +test_when(void **state) +{ + struct lysp_when *w = NULL; + + PARSER_CUR_PMOD(YCTX)->version = 2; /* simulate YANG 1.1 */ + + in.current = "l { description text1;description text2;} ..."; + assert_int_equal(LY_EVALID, parse_when(YCTX, &w)); + assert_null(w); + CHECK_LOG_CTX("Duplicate keyword \"description\".", NULL, 1); + + in.current = "l { reference 1;reference 2;} ..."; + assert_int_equal(LY_EVALID, parse_when(YCTX, &w)); + assert_null(w); + CHECK_LOG_CTX("Duplicate keyword \"reference\".", NULL, 1); +} + +static void +test_value(void **state) +{ + struct lysp_type_enum enm; + + in.current = "-0;"; + memset(&enm, 0, sizeof enm); + assert_int_equal(parse_type_enum_value_pos(YCTX, LY_STMT_VALUE, &enm), LY_SUCCESS); + + in.current = "-0;"; + memset(&enm, 0, sizeof enm); + assert_int_equal(parse_type_enum_value_pos(YCTX, LY_STMT_POSITION, &enm), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-0\" of \"position\".", NULL, 1); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_helpers, setup, teardown), + UTEST(test_comments, setup, teardown), + UTEST(test_arg, setup, teardown), + UTEST(test_stmts, setup, teardown), + UTEST(test_minmax, setup, teardown), + UTEST(test_valid_module, setup, teardown), + UTEST(test_module, setup, teardown), + UTEST(test_deviation, setup, teardown), + UTEST(test_deviate, setup, teardown), + UTEST(test_container, setup, teardown), + UTEST(test_leaf, setup, teardown), + UTEST(test_leaflist, setup, teardown), + UTEST(test_list, setup, teardown), + UTEST(test_choice, setup, teardown), + UTEST(test_case, setup, teardown), + UTEST(test_anydata, setup, teardown), + UTEST(test_anyxml, setup, teardown), + UTEST(test_action, setup, teardown), + UTEST(test_notification, setup, teardown), + UTEST(test_grouping, setup, teardown), + UTEST(test_uses, setup, teardown), + UTEST(test_augment, setup, teardown), + UTEST(test_when, setup, teardown), + UTEST(test_value, setup, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/schema/test_yin.c b/tests/utests/schema/test_yin.c new file mode 100644 index 0000000..329fcda --- /dev/null +++ b/tests/utests/schema/test_yin.c @@ -0,0 +1,3589 @@ +/** + * @file test_yin.c + * @author David Sedlák <xsedla1d@stud.fit.vutbr.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for YIN parser and printer + * + * Copyright (c) 2015 - 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 <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "in.h" +#include "ly_common.h" +#include "parser_internal.h" +#include "schema_compile.h" +#include "tree.h" +#include "tree_edit.h" +#include "tree_schema.h" +#include "tree_schema_internal.h" +#include "xml.h" +#include "xpath.h" + +/* copied from parser_yin.c */ +enum yin_argument { + YIN_ARG_UNKNOWN = 0, /**< parsed argument can not be matched with any supported yin argument keyword */ + YIN_ARG_NAME, /**< argument name */ + YIN_ARG_TARGET_NODE, /**< argument target-node */ + YIN_ARG_MODULE, /**< argument module */ + YIN_ARG_VALUE, /**< argument value */ + YIN_ARG_TEXT, /**< argument text */ + YIN_ARG_CONDITION, /**< argument condition */ + YIN_ARG_URI, /**< argument uri */ + YIN_ARG_DATE, /**< argument data */ + YIN_ARG_TAG, /**< argument tag */ + YIN_ARG_NONE /**< empty (special value) */ +}; + +struct yin_subelement { + enum ly_stmt type; /**< type of keyword */ + void *dest; /**< meta infromation passed to responsible function (mostly information about where parsed subelement should be stored) */ + uint16_t flags; /**< describes constraints of subelement can be set to YIN_SUBELEM_MANDATORY, YIN_SUBELEM_UNIQUE, YIN_SUBELEM_FIRST, YIN_SUBELEM_VER2, and YIN_SUBELEM_DEFAULT_TEXT */ +}; + +struct import_meta { + const char *prefix; /**< module prefix. */ + struct lysp_import **imports; /**< imports to add to. */ +}; + +struct yin_argument_meta { + uint16_t *flags; /**< Argument flags */ + const char **argument; /**< Argument value */ +}; + +struct tree_node_meta { + struct lysp_node *parent; /**< parent node */ + struct lysp_node **nodes; /**< linked list of siblings */ +}; + +struct include_meta { + const char *name; /**< Module/submodule name. */ + struct lysp_include **includes; /**< [Sized array](@ref sizedarrays) of parsed includes to add to. */ +}; + +struct inout_meta { + struct lysp_node *parent; /**< Parent node. */ + struct lysp_node_action_inout *inout_p; /**< inout_p Input/output pointer to write to. */ +}; + +struct minmax_dev_meta { + uint32_t *lim; /**< min/max value to write to. */ + uint16_t *flags; /**< min/max flags to write to. */ + struct lysp_ext_instance **exts; /**< extension instances to add to. */ +}; + +#define YIN_SUBELEM_MANDATORY 0x01 +#define YIN_SUBELEM_UNIQUE 0x02 +#define YIN_SUBELEM_FIRST 0x04 +#define YIN_SUBELEM_VER2 0x08 + +#define YIN_SUBELEM_PARSED 0x80 + +/* prototypes of static functions */ +enum yin_argument yin_match_argument_name(const char *name, size_t len); + +LY_ERR yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info, size_t subelem_info_size, + const void *parent, enum ly_stmt parent_stmt, const char **text_content, struct lysp_ext_instance **exts); +LY_ERR yin_validate_value(struct lysp_yin_ctx *ctx, enum yang_arg val_type); +enum ly_stmt yin_match_keyword(struct lysp_yin_ctx *ctx, const char *name, size_t name_len, + const char *prefix, size_t prefix_len, enum ly_stmt parrent); + +LY_ERR yin_parse_extension_instance(struct lysp_yin_ctx *ctx, const void *parent, enum ly_stmt parent_stmt, + LY_ARRAY_COUNT_TYPE parent_stmt_index, struct lysp_ext_instance **exts); +LY_ERR yin_parse_element_generic(struct lysp_yin_ctx *ctx, enum ly_stmt parent, struct lysp_stmt **element); +LY_ERR yin_parse_mod(struct lysp_yin_ctx *ctx, struct lysp_module *mod); +LY_ERR yin_parse_submod(struct lysp_yin_ctx *ctx, struct lysp_submodule *submod); + +/* wrapping element used for mocking has nothing to do with real module structure */ +#define ELEMENT_WRAPPER_START "<status xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">" +#define ELEMENT_WRAPPER_END "</status>" + +#define TEST_1_CHECK_LYSP_EXT_INSTANCE(NODE, INSUBSTMT)\ + CHECK_LYSP_EXT_INSTANCE((NODE), NULL, 1, INSUBSTMT, 0, "myext:c-define", LY_VALUE_XML) + +struct lysp_yin_ctx *YCTX; +struct lysf_ctx fctx; + +static int +setup_ctx(void **state) +{ + struct lysp_module *pmod; + + /* allocate parser context */ + YCTX = calloc(1, sizeof(*YCTX)); + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + YCTX->format = LYS_IN_YIN; + ly_set_new(&YCTX->parsed_mods); + + /* allocate new parsed module */ + pmod = calloc(1, sizeof *pmod); + ly_set_add(YCTX->parsed_mods, pmod, 1, NULL); + + /* allocate new module */ + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->ctx = UTEST_LYCTX; + pmod->mod->parsed = pmod; + + return 0; +} + +static int +setup(void **state) +{ + UTEST_SETUP; + + setup_ctx(state); + + fctx.ctx = UTEST_LYCTX; + fctx.mod = PARSER_CUR_PMOD(YCTX)->mod; + + return 0; +} + +static int +teardown_ctx(void **UNUSED(state)) +{ + lys_module_free(&fctx, PARSER_CUR_PMOD(YCTX)->mod, 0); + lysp_yin_ctx_free(YCTX); + YCTX = NULL; + + return 0; +} + +static int +teardown(void **state) +{ + teardown_ctx(state); + + lysf_ctx_erase(&fctx); + + UTEST_TEARDOWN; + + return 0; +} + +#define RESET_STATE \ + ly_in_free(UTEST_IN, 0); \ + UTEST_IN = NULL; \ + teardown_ctx(state); \ + setup_ctx(state) + +static void +test_yin_match_keyword(void **state) +{ + const char *prefix; + size_t prefix_len; + + /* create mock yin namespace in xml context */ + ly_in_new_memory("<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" />", &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + prefix = YCTX->xmlctx->prefix; + prefix_len = YCTX->xmlctx->prefix_len; + + assert_int_equal(yin_match_keyword(YCTX, "anydatax", strlen("anydatax"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE); + assert_int_equal(yin_match_keyword(YCTX, "asdasd", strlen("asdasd"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE); + assert_int_equal(yin_match_keyword(YCTX, "", 0, prefix, prefix_len, LY_STMT_NONE), LY_STMT_NONE); + assert_int_equal(yin_match_keyword(YCTX, "anydata", strlen("anydata"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ANYDATA); + assert_int_equal(yin_match_keyword(YCTX, "anyxml", strlen("anyxml"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ANYXML); + assert_int_equal(yin_match_keyword(YCTX, "argument", strlen("argument"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ARGUMENT); + assert_int_equal(yin_match_keyword(YCTX, "augment", strlen("augment"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_AUGMENT); + assert_int_equal(yin_match_keyword(YCTX, "base", strlen("base"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BASE); + assert_int_equal(yin_match_keyword(YCTX, "belongs-to", strlen("belongs-to"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BELONGS_TO); + assert_int_equal(yin_match_keyword(YCTX, "bit", strlen("bit"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_BIT); + assert_int_equal(yin_match_keyword(YCTX, "case", strlen("case"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CASE); + assert_int_equal(yin_match_keyword(YCTX, "choice", strlen("choice"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CHOICE); + assert_int_equal(yin_match_keyword(YCTX, "config", strlen("config"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONFIG); + assert_int_equal(yin_match_keyword(YCTX, "contact", strlen("contact"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONTACT); + assert_int_equal(yin_match_keyword(YCTX, "container", strlen("container"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_CONTAINER); + assert_int_equal(yin_match_keyword(YCTX, "default", strlen("default"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEFAULT); + assert_int_equal(yin_match_keyword(YCTX, "description", strlen("description"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DESCRIPTION); + assert_int_equal(yin_match_keyword(YCTX, "deviate", strlen("deviate"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEVIATE); + assert_int_equal(yin_match_keyword(YCTX, "deviation", strlen("deviation"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_DEVIATION); + assert_int_equal(yin_match_keyword(YCTX, "enum", strlen("enum"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ENUM); + assert_int_equal(yin_match_keyword(YCTX, "error-app-tag", strlen("error-app-tag"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ERROR_APP_TAG); + assert_int_equal(yin_match_keyword(YCTX, "error-message", strlen("error-message"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ERROR_MESSAGE); + assert_int_equal(yin_match_keyword(YCTX, "extension", strlen("extension"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_EXTENSION); + assert_int_equal(yin_match_keyword(YCTX, "feature", strlen("feature"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_FEATURE); + assert_int_equal(yin_match_keyword(YCTX, "fraction-digits", strlen("fraction-digits"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_FRACTION_DIGITS); + assert_int_equal(yin_match_keyword(YCTX, "grouping", strlen("grouping"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_GROUPING); + assert_int_equal(yin_match_keyword(YCTX, "identity", strlen("identity"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IDENTITY); + assert_int_equal(yin_match_keyword(YCTX, "if-feature", strlen("if-feature"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IF_FEATURE); + assert_int_equal(yin_match_keyword(YCTX, "import", strlen("import"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_IMPORT); + assert_int_equal(yin_match_keyword(YCTX, "include", strlen("include"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_INCLUDE); + assert_int_equal(yin_match_keyword(YCTX, "input", strlen("input"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_INPUT); + assert_int_equal(yin_match_keyword(YCTX, "key", strlen("key"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_KEY); + assert_int_equal(yin_match_keyword(YCTX, "leaf", strlen("leaf"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LEAF); + assert_int_equal(yin_match_keyword(YCTX, "leaf-list", strlen("leaf-list"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LEAF_LIST); + assert_int_equal(yin_match_keyword(YCTX, "length", strlen("length"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LENGTH); + assert_int_equal(yin_match_keyword(YCTX, "list", strlen("list"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_LIST); + assert_int_equal(yin_match_keyword(YCTX, "mandatory", strlen("mandatory"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MANDATORY); + assert_int_equal(yin_match_keyword(YCTX, "max-elements", strlen("max-elements"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MAX_ELEMENTS); + assert_int_equal(yin_match_keyword(YCTX, "min-elements", strlen("min-elements"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MIN_ELEMENTS); + assert_int_equal(yin_match_keyword(YCTX, "modifier", strlen("modifier"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MODIFIER); + assert_int_equal(yin_match_keyword(YCTX, "module", strlen("module"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MODULE); + assert_int_equal(yin_match_keyword(YCTX, "must", strlen("must"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_MUST); + assert_int_equal(yin_match_keyword(YCTX, "namespace", strlen("namespace"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NAMESPACE); + assert_int_equal(yin_match_keyword(YCTX, "notification", strlen("notification"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_NOTIFICATION); + assert_int_equal(yin_match_keyword(YCTX, "ordered-by", strlen("ordered-by"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ORDERED_BY); + assert_int_equal(yin_match_keyword(YCTX, "organization", strlen("organization"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_ORGANIZATION); + assert_int_equal(yin_match_keyword(YCTX, "output", strlen("output"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_OUTPUT); + assert_int_equal(yin_match_keyword(YCTX, "path", strlen("path"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PATH); + assert_int_equal(yin_match_keyword(YCTX, "pattern", strlen("pattern"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PATTERN); + assert_int_equal(yin_match_keyword(YCTX, "position", strlen("position"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_POSITION); + assert_int_equal(yin_match_keyword(YCTX, "prefix", strlen("prefix"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PREFIX); + assert_int_equal(yin_match_keyword(YCTX, "presence", strlen("presence"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_PRESENCE); + assert_int_equal(yin_match_keyword(YCTX, "range", strlen("range"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_RANGE); + assert_int_equal(yin_match_keyword(YCTX, "reference", strlen("reference"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REFERENCE); + assert_int_equal(yin_match_keyword(YCTX, "refine", strlen("refine"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REFINE); + assert_int_equal(yin_match_keyword(YCTX, "require-instance", strlen("require-instance"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REQUIRE_INSTANCE); + assert_int_equal(yin_match_keyword(YCTX, "revision", strlen("revision"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REVISION); + assert_int_equal(yin_match_keyword(YCTX, "revision-date", strlen("revision-date"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_REVISION_DATE); + assert_int_equal(yin_match_keyword(YCTX, "rpc", strlen("rpc"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_RPC); + assert_int_equal(yin_match_keyword(YCTX, "status", strlen("status"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_STATUS); + assert_int_equal(yin_match_keyword(YCTX, "submodule", strlen("submodule"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_SUBMODULE); + assert_int_equal(yin_match_keyword(YCTX, "type", strlen("type"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_TYPE); + assert_int_equal(yin_match_keyword(YCTX, "typedef", strlen("typedef"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_TYPEDEF); + assert_int_equal(yin_match_keyword(YCTX, "unique", strlen("unique"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_UNIQUE); + assert_int_equal(yin_match_keyword(YCTX, "units", strlen("units"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_UNITS); + assert_int_equal(yin_match_keyword(YCTX, "uses", strlen("uses"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_USES); + assert_int_equal(yin_match_keyword(YCTX, "value", strlen("value"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_VALUE); + assert_int_equal(yin_match_keyword(YCTX, "when", strlen("when"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_WHEN); + assert_int_equal(yin_match_keyword(YCTX, "yang-version", strlen("yang-version"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_YANG_VERSION); + assert_int_equal(yin_match_keyword(YCTX, "yin-element", strlen("yin-element"), prefix, prefix_len, LY_STMT_NONE), LY_STMT_YIN_ELEMENT); +} + +static void +test_yin_match_argument_name(void **UNUSED(state)) +{ + assert_int_equal(yin_match_argument_name("", 5), YIN_ARG_UNKNOWN); + assert_int_equal(yin_match_argument_name("qwertyasd", 5), YIN_ARG_UNKNOWN); + assert_int_equal(yin_match_argument_name("conditionasd", 8), YIN_ARG_UNKNOWN); + assert_int_equal(yin_match_argument_name("condition", 9), YIN_ARG_CONDITION); + assert_int_equal(yin_match_argument_name("date", 4), YIN_ARG_DATE); + assert_int_equal(yin_match_argument_name("module", 6), YIN_ARG_MODULE); + assert_int_equal(yin_match_argument_name("name", 4), YIN_ARG_NAME); + assert_int_equal(yin_match_argument_name("tag", 3), YIN_ARG_TAG); + assert_int_equal(yin_match_argument_name("target-node", 11), YIN_ARG_TARGET_NODE); + assert_int_equal(yin_match_argument_name("text", 4), YIN_ARG_TEXT); + assert_int_equal(yin_match_argument_name("uri", 3), YIN_ARG_URI); + assert_int_equal(yin_match_argument_name("value", 5), YIN_ARG_VALUE); +} + +static void +test_yin_parse_content(void **state) +{ + LY_ERR ret = LY_SUCCESS; + const char *data = + "<prefix value=\"a_mod\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <myext:custom xmlns:myext=\"urn:example:extensions\">totally amazing extension</myext:custom>\n" + " <extension name=\"ext\">\n" + " <argument name=\"argname\"></argument>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"></status>\n" + " </extension>\n" + " <text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>\n" + " <if-feature name=\"foo\"></if-feature>\n" + " <when condition=\"condition...\">\n" + " <reference><text>when_ref</text></reference>\n" + " <description><text>when_desc</text></description>\n" + " </when>\n" + " <config value=\"true\"/>\n" + " <error-message>\n" + " <value>error-msg</value>\n" + " </error-message>\n" + " <error-app-tag value=\"err-app-tag\"/>\n" + " <units name=\"radians\"></units>\n" + " <default value=\"default-value\"/>\n" + " <position value=\"25\"></position>\n" + " <value value=\"-5\"/>\n" + " <require-instance value=\"true\"></require-instance>\n" + " <range value=\"5..10\" />\n" + " <length value=\"baf\"/>\n" + " <pattern value='pattern'>\n" + " <modifier value='invert-match'/>\n" + " </pattern>\n" + " <enum name=\"yay\">\n" + " </enum>\n" + "</prefix>"; + struct lysp_ext_instance *exts = NULL; + const char *value; + + /* test unique subelem */ + const char *prefix_value; + struct yin_subelement subelems2[2] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_UNIQUE}, + {LY_STMT_ARG_TEXT, &value, YIN_SUBELEM_UNIQUE}}; + + data = ELEMENT_WRAPPER_START + "<prefix value=\"inv_mod\" />" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + ELEMENT_WRAPPER_END; + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems2, 2, NULL, LY_STMT_STATUS, NULL, &exts); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Redefinition of \"text\" sub-element in \"status\" element.", NULL, 1); + lydict_remove(UTEST_LYCTX, prefix_value); + lydict_remove(UTEST_LYCTX, value); + RESET_STATE; + + /* test first subelem */ + data = ELEMENT_WRAPPER_START + "<prefix value=\"inv_mod\" />" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>" + ELEMENT_WRAPPER_END; + struct yin_subelement subelems3[2] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_UNIQUE}, + {LY_STMT_ARG_TEXT, &value, YIN_SUBELEM_FIRST}}; + + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems3, 2, NULL, LY_STMT_STATUS, NULL, &exts); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Sub-element \"text\" of \"status\" element must be defined as it's first sub-element.", NULL, 1); + lydict_remove(UTEST_LYCTX, prefix_value); + RESET_STATE; + + /* test mandatory subelem */ + data = ELEMENT_WRAPPER_START ELEMENT_WRAPPER_END; + struct yin_subelement subelems4[1] = {{LY_STMT_PREFIX, &prefix_value, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE}}; + + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems4, 1, NULL, LY_STMT_STATUS, NULL, &exts); + assert_int_equal(ret, LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"prefix\" of \"status\" element.", NULL, 1); +} + +static void +test_validate_value(void **state) +{ + const char *data = ELEMENT_WRAPPER_START ELEMENT_WRAPPER_END; + + /* create some XML context */ + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + YCTX->xmlctx->status = LYXML_ELEM_CONTENT; + YCTX->xmlctx->dynamic = 0; + + YCTX->xmlctx->value = "#invalid"; + YCTX->xmlctx->value_len = 8; + assert_int_equal(yin_validate_value(YCTX, Y_IDENTIF_ARG), LY_EVALID); + CHECK_LOG_CTX("Invalid identifier first character '#' (0x0023).", NULL, 1); + + YCTX->xmlctx->value = ""; + YCTX->xmlctx->value_len = 0; + assert_int_equal(yin_validate_value(YCTX, Y_STR_ARG), LY_SUCCESS); + + YCTX->xmlctx->value = "pre:b"; + YCTX->xmlctx->value_len = 5; + assert_int_equal(yin_validate_value(YCTX, Y_IDENTIF_ARG), LY_EVALID); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); + assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_SUCCESS); + + YCTX->xmlctx->value = "pre:pre:b"; + YCTX->xmlctx->value_len = 9; + assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_EVALID); + CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", NULL, 1); +} + +static void +test_valid_module(void **state) +{ + struct lys_module *mod; + char *printed; + const char *links_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"links\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:mod2=\"urn:module2\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:module2\"/>\n" + " <prefix value=\"mod2\"/>\n" + " <identity name=\"just-another-identity\"/>\n" + " <grouping name=\"rgroup\">\n" + " <leaf name=\"rg1\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"rg2\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </grouping>\n" + " <leaf name=\"one-leaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <list name=\"list-for-augment\">\n" + " <key value=\"keyleaf\"/>\n" + " <leaf name=\"keyleaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"just-leaf\">\n" + " <type name=\"int32\"/>\n" + " </leaf>\n" + " </list>\n" + " <leaf name=\"rleaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf-list name=\"llist\">\n" + " <type name=\"string\"/>\n" + " <min-elements value=\"0\"/>\n" + " <max-elements value=\"100\"/>\n" + " <ordered-by value=\"user\"/>\n" + " </leaf-list>\n" + "</module>\n"; + const char *statements_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"statements\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:mod=\"urn:module\"\n" + " xmlns:mod2=\"urn:module2\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:module\"/>\n" + " <prefix value=\"mod\"/>\n" + " <import module=\"links\">\n" + " <prefix value=\"mod2\"/>\n" + " </import>\n" + " <extension name=\"ext\"/>\n" + " <identity name=\"random-identity\">\n" + " <base name=\"mod2:just-another-identity\"/>\n" + " <base name=\"another-identity\"/>\n" + " </identity>\n" + " <identity name=\"another-identity\">\n" + " <base name=\"mod2:just-another-identity\"/>\n" + " </identity>\n" + " <typedef name=\"percent\">\n" + " <type name=\"uint8\">\n" + " <range value=\"0 .. 100\"/>\n" + " </type>\n" + " <units name=\"percent\"/>\n" + " </typedef>\n" + " <list name=\"list1\">\n" + " <key value=\"a\"/>\n" + " <leaf name=\"a\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"x\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"y\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </list>\n" + " <container name=\"ice-cream-shop\">\n" + " <container name=\"employees\">\n" + " <when condition=\"/list1/x\"/>\n" + " <list name=\"employee\">\n" + " <key value=\"id\"/>\n" + " <unique tag=\"name\"/>\n" + " <config value=\"true\"/>\n" + " <min-elements value=\"0\">\n" + " <mod:ext/>\n" + " </min-elements>\n" + " <max-elements value=\"unbounded\"/>\n" + " <leaf name=\"id\">\n" + " <type name=\"uint64\"/>\n" + " <mandatory value=\"true\"/>\n" + " </leaf>\n" + " <leaf name=\"name\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"age\">\n" + " <type name=\"uint32\"/>\n" + " </leaf>\n" + " </list>\n" + " </container>\n" + " </container>\n" + " <container name=\"random\">\n" + " <grouping name=\"group\">\n" + " <leaf name=\"g1\">\n" + " <type name=\"percent\"/>\n" + " <mandatory value=\"false\"/>\n" + " </leaf>\n" + " <leaf name=\"g2\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </grouping>\n" + " <choice name=\"switch\">\n" + " <case name=\"a\">\n" + " <leaf name=\"aleaf\">\n" + " <type name=\"string\"/>\n" + " <default value=\"aaa\"/>\n" + " </leaf>\n" + " </case>\n" + " <case name=\"c\">\n" + " <leaf name=\"cleaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </case>\n" + " </choice>\n" + " <anyxml name=\"xml-data\"/>\n" + " <anydata name=\"any-data\"/>\n" + " <leaf-list name=\"leaflist\">\n" + " <type name=\"string\"/>\n" + " <min-elements value=\"0\"/>\n" + " <max-elements value=\"20\"/>\n" + " </leaf-list>\n" + " <uses name=\"group\"/>\n" + " <uses name=\"mod2:rgroup\"/>\n" + " <leaf name=\"lref\">\n" + " <type name=\"leafref\">\n" + " <path value=\"/mod2:one-leaf\"/>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"iref\">\n" + " <type name=\"identityref\">\n" + " <base name=\"mod2:just-another-identity\"/>\n" + " </type>\n" + " </leaf>\n" + " </container>\n" + " <augment target-node=\"/random\">\n" + " <leaf name=\"aug-leaf\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </augment>\n" + " <notification name=\"notif\"/>\n" + " <deviation target-node=\"/mod:ice-cream-shop/mod:employees/mod:employee/mod:age\">\n" + " <deviate value=\"not-supported\">\n" + " <mod:ext/>\n" + " </deviate>\n" + " </deviation>\n" + " <deviation target-node=\"/mod:list1\">\n" + " <deviate value=\"add\">\n" + " <mod:ext/>\n" + " <must condition=\"1\"/>\n" + " <must condition=\"2\"/>\n" + " <unique tag=\"x\"/>\n" + " <unique tag=\"y\"/>\n" + " <config value=\"true\"/>\n" + " <min-elements value=\"1\"/>\n" + " <max-elements value=\"2\"/>\n" + " </deviate>\n" + " </deviation>\n" + " <deviation target-node=\"/mod:ice-cream-shop/mod:employees/mod:employee\">\n" + " <deviate value=\"delete\">\n" + " <unique tag=\"name\"/>\n" + " </deviate>\n" + " </deviation>\n" + " <deviation target-node=\"/mod:random/mod:leaflist\">\n" + " <deviate value=\"replace\">\n" + " <type name=\"uint32\"/>\n" + " <min-elements value=\"10\"/>\n" + " <max-elements value=\"15\"/>\n" + " </deviate>\n" + " </deviation>\n" + "</module>\n"; + + UTEST_ADD_MODULE(links_yin, LYS_IN_YIN, NULL, NULL); + UTEST_ADD_MODULE(statements_yin, LYS_IN_YIN, NULL, &mod); + lys_print_mem(&printed, mod, LYS_OUT_YIN, 0); + assert_string_equal(printed, statements_yin); + free(printed); +} + +static void +test_print_module(void **state) +{ + struct lys_module *mod; + + char *orig = malloc(8096); + + strcpy(orig, + "module all {\n" + " yang-version 1.1;\n" + " namespace \"urn:all\";\n" + " prefix all_mod;\n\n" + " import ietf-yang-types {\n" + " prefix yt;\n" + " revision-date 2013-07-15;\n" + " description\n" + " \"YANG types\";\n" + " reference\n" + " \"RFC reference\";\n" + " }\n\n" + " feature feat1 {\n" + " if-feature \"feat2\";\n" + " status obsolete;\n" + " }\n\n" + " feature feat2;\n" + " feature feat3;\n\n" + " identity ident2 {\n" + " base ident1;\n" + " }\n\n" + " identity ident1;\n\n" + " typedef tdef1 {\n" + " type tdef2 {\n" + " length \"3..9 | 30..40\";\n" + " pattern \"[ac]*\";\n" + " }\n" + " units \"none\";\n" + " default \"aaa\";\n" + " }\n\n" + " typedef tdef2 {\n" + " type string {\n" + " length \"2..10 | 20..50\";\n" + " pattern \"[ab]*\";\n" + " }\n" + " }\n\n" + " grouping group1 {\n" + " leaf leaf1 {\n" + " type int8;\n" + " }\n" + " }\n\n" + " container cont1 {\n" + " leaf leaf2 {\n" + " if-feature \"feat1\";\n" + " type int16;\n" + " status obsolete;\n" + " }\n\n" + " uses group1 {\n" + " if-feature \"feat2\";\n" + " refine \"leaf1\" {\n" + " if-feature \"feat3\";\n" + " must \"24 - 4 = number('20')\";\n" + " default \"25\";\n" + " config true;\n" + " mandatory false;\n" + " description\n" + " \"dsc\";\n" + " reference\n" + " \"none\";\n" + " }\n" + " }\n\n" + " leaf leaf3 {\n" + " type int32;\n" + " }\n\n" + " leaf leaf4 {\n" + " type int64 {\n" + " range \"1000 .. 50000\" {\n" + " error-message\n" + " \"Special error message.\";\n" + " error-app-tag \"special-tag\";\n" + " }\n" + " }\n" + " }\n\n" + " leaf leaf5 {\n" + " type uint8;\n" + " }\n\n" + " leaf leaf6 {\n" + " type uint16;\n" + " }\n\n" + " leaf leaf7 {\n" + " type uint32;\n" + " }\n\n" + " leaf leaf8 {\n" + " type uint64;\n" + " }\n\n" + " choice choic1 {\n" + " default \"leaf9b\";\n" + " leaf leaf9a {\n" + " type decimal64 {\n" + " fraction-digits 9;\n" + " }\n" + " }\n\n" + " leaf leaf9b {\n" + " type boolean;\n" + " default \"false\";\n" + " }\n" + " }\n\n" + " leaf leaf10 {\n" + " type boolean;\n" + " }\n\n"); + strcpy(orig + strlen(orig), + " leaf leaf11 {\n" + " type enumeration {\n" + " enum \"one\";\n" + " enum \"two\";\n" + " enum \"five\" {\n" + " value 5;\n" + " }\n" + " }\n" + " }\n\n" + " leaf leaf12 {\n" + " type bits {\n" + " bit flag0 {\n" + " position 0;\n" + " }\n" + " bit flag1;\n" + " bit flag2 {\n" + " position 2;\n" + " }\n" + " bit flag3 {\n" + " position 3;\n" + " }\n" + " }\n" + " default \"flag0 flag3\";\n" + " }\n\n" + " leaf leaf13 {\n" + " type binary;\n" + " }\n\n" + " leaf leaf14 {\n" + " type leafref {\n" + " path \"/cont1/leaf17\";\n" + " }\n" + " }\n\n" + " leaf leaf15 {\n" + " type empty;\n" + " }\n\n" + " leaf leaf16 {\n" + " type union {\n" + " type instance-identifier {\n" + " require-instance true;\n" + " }\n" + " type int8;\n" + " }\n" + " }\n\n" + " list list1 {\n" + " key \"leaf18\";\n" + " unique \"leaf19\";\n" + " min-elements 1;\n" + " max-elements 20;\n" + " leaf leaf18 {\n" + " type string;\n" + " }\n\n" + " leaf leaf19 {\n" + " type uint32;\n" + " }\n\n" + " anyxml axml1;\n" + " anydata adata1;\n\n" + " action act1 {\n" + " input {\n" + " leaf leaf24 {\n" + " type string;\n" + " }\n" + " }\n\n" + " output {\n" + " leaf leaf25 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n\n" + " notification notif1 {\n" + " leaf leaf26 {\n" + " type string;\n" + " }\n" + " }\n" + " }\n\n" + " leaf-list llist1 {\n" + " type tdef1;\n" + " ordered-by user;\n" + " }\n\n" + " list list2 {\n" + " key \"leaf27 leaf28\";\n" + " leaf leaf27 {\n" + " type uint8;\n" + " }\n\n" + " leaf leaf28 {\n" + " type uint8;\n" + " }\n" + " }\n\n" + " leaf leaf29 {\n" + " type instance-identifier;\n" + " }\n\n" + " container must-deviations-container {\n" + " presence \"Allows deviations on the leaf\";\n" + " leaf leaf30 {\n" + " type string;\n" + " }\n" + " }\n\n" + " leaf leaf23 {\n" + " type empty;\n" + " }\n" + " }\n\n" + " augment \"/cont1\" {\n" + " leaf leaf17 {\n" + " type string;\n" + " }\n" + " }\n\n" + " rpc rpc1 {\n" + " input {\n" + " leaf leaf20 {\n" + " type tdef1;\n" + " }\n" + " }\n\n" + " output {\n" + " container cont2 {\n" + " leaf leaf21 {\n" + " type empty;\n" + " }\n" + " }\n" + " }\n" + " }\n\n" + " container test-when {\n" + " leaf when-check {\n" + " type boolean;\n" + " }\n\n" + " leaf gated-data {\n" + " when \"../when-check = 'true'\";\n" + " type uint16;\n" + " }\n" + " }\n\n" + " extension c-define {\n" + " description\n" + " \"Takes as an argument a name string.\n" + " Makes the code generator use the given name\n" + " in the #define.\";\n" + " argument \"name\";\n" + " }\n" + "}\n"); + + char *ori_res = malloc(8096); + + strcpy(ori_res, + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"all\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:all_mod=\"urn:all\"\n" + " xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:all\"/>\n" + " <prefix value=\"all_mod\"/>\n" + " <import module=\"ietf-yang-types\">\n" + " <prefix value=\"yt\"/>\n" + " <revision-date date=\"2013-07-15\"/>\n" + " <description>\n" + " <text>YANG types</text>\n" + " </description>\n" + " <reference>\n" + " <text>RFC reference</text>\n" + " </reference>\n" + " </import>\n" + " <extension name=\"c-define\">\n" + " <argument name=\"name\"/>\n" + " <description>\n" + " <text>Takes as an argument a name string.\n" + "Makes the code generator use the given name\n" + "in the #define.</text>\n" + " </description>\n" + " </extension>\n" + " <feature name=\"feat1\">\n" + " <if-feature name=\"feat2\"/>\n" + " <status value=\"obsolete\"/>\n" + " </feature>\n" + " <feature name=\"feat2\"/>\n" + " <feature name=\"feat3\"/>\n" + " <identity name=\"ident2\">\n" + " <base name=\"ident1\"/>\n" + " </identity>\n" + " <identity name=\"ident1\"/>\n" + " <typedef name=\"tdef1\">\n" + " <type name=\"tdef2\">\n" + " <length value=\"3..9 | 30..40\"/>\n" + " <pattern value=\"[ac]*\"/>\n" + " </type>\n" + " <units name=\"none\"/>\n" + " <default value=\"aaa\"/>\n" + " </typedef>\n" + " <typedef name=\"tdef2\">\n" + " <type name=\"string\">\n" + " <length value=\"2..10 | 20..50\"/>\n" + " <pattern value=\"[ab]*\"/>\n" + " </type>\n" + " </typedef>\n" + " <grouping name=\"group1\">\n" + " <leaf name=\"leaf1\">\n" + " <type name=\"int8\"/>\n" + " </leaf>\n" + " </grouping>\n" + " <container name=\"cont1\">\n" + " <leaf name=\"leaf2\">\n" + " <if-feature name=\"feat1\"/>\n" + " <type name=\"int16\"/>\n" + " <status value=\"obsolete\"/>\n" + " </leaf>\n" + " <uses name=\"group1\">\n" + " <if-feature name=\"feat2\"/>\n" + " <refine target-node=\"leaf1\">\n" + " <if-feature name=\"feat3\"/>\n" + " <must condition=\"24 - 4 = number('20')\"/>\n" + " <default value=\"25\"/>\n" + " <config value=\"true\"/>\n" + " <mandatory value=\"false\"/>\n" + " <description>\n" + " <text>dsc</text>\n" + " </description>\n" + " <reference>\n" + " <text>none</text>\n" + " </reference>\n" + " </refine>\n" + " </uses>\n" + " <leaf name=\"leaf3\">\n" + " <type name=\"int32\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf4\">\n" + " <type name=\"int64\">\n" + " <range value=\"1000 .. 50000\">\n" + " <error-message>\n" + " <value>Special error message.</value>\n" + " </error-message>\n" + " <error-app-tag value=\"special-tag\"/>\n" + " </range>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf5\">\n" + " <type name=\"uint8\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf6\">\n" + " <type name=\"uint16\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf7\">\n" + " <type name=\"uint32\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf8\">\n" + " <type name=\"uint64\"/>\n" + " </leaf>\n" + " <choice name=\"choic1\">\n" + " <default value=\"leaf9b\"/>\n" + " <leaf name=\"leaf9a\">\n" + " <type name=\"decimal64\">\n" + " <fraction-digits value=\"9\"/>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf9b\">\n" + " <type name=\"boolean\"/>\n" + " <default value=\"false\"/>\n" + " </leaf>\n" + " </choice>\n" + " <leaf name=\"leaf10\">\n" + " <type name=\"boolean\"/>\n" + " </leaf>\n"); + strcpy(ori_res + strlen(ori_res), + " <leaf name=\"leaf11\">\n" + " <type name=\"enumeration\">\n" + " <enum name=\"one\"/>\n" + " <enum name=\"two\"/>\n" + " <enum name=\"five\">\n" + " <value value=\"5\"/>\n" + " </enum>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf12\">\n" + " <type name=\"bits\">\n" + " <bit name=\"flag0\">\n" + " <position value=\"0\"/>\n" + " </bit>\n" + " <bit name=\"flag1\"/>\n" + " <bit name=\"flag2\">\n" + " <position value=\"2\"/>\n" + " </bit>\n" + " <bit name=\"flag3\">\n" + " <position value=\"3\"/>\n" + " </bit>\n" + " </type>\n" + " <default value=\"flag0 flag3\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf13\">\n" + " <type name=\"binary\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf14\">\n" + " <type name=\"leafref\">\n" + " <path value=\"/cont1/leaf17\"/>\n" + " </type>\n" + " </leaf>\n" + " <leaf name=\"leaf15\">\n" + " <type name=\"empty\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf16\">\n" + " <type name=\"union\">\n" + " <type name=\"instance-identifier\">\n" + " <require-instance value=\"true\"/>\n" + " </type>\n" + " <type name=\"int8\"/>\n" + " </type>\n" + " </leaf>\n" + " <list name=\"list1\">\n" + " <key value=\"leaf18\"/>\n" + " <unique tag=\"leaf19\"/>\n" + " <min-elements value=\"1\"/>\n" + " <max-elements value=\"20\"/>\n" + " <leaf name=\"leaf18\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf19\">\n" + " <type name=\"uint32\"/>\n" + " </leaf>\n" + " <anyxml name=\"axml1\"/>\n" + " <anydata name=\"adata1\"/>\n" + " <action name=\"act1\">\n" + " <input>\n" + " <leaf name=\"leaf24\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </input>\n" + " <output>\n" + " <leaf name=\"leaf25\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </output>\n" + " </action>\n" + " <notification name=\"notif1\">\n" + " <leaf name=\"leaf26\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </notification>\n" + " </list>\n" + " <leaf-list name=\"llist1\">\n" + " <type name=\"tdef1\"/>\n" + " <ordered-by value=\"user\"/>\n" + " </leaf-list>\n" + " <list name=\"list2\">\n" + " <key value=\"leaf27 leaf28\"/>\n" + " <leaf name=\"leaf27\">\n" + " <type name=\"uint8\"/>\n" + " </leaf>\n" + " <leaf name=\"leaf28\">\n" + " <type name=\"uint8\"/>\n" + " </leaf>\n" + " </list>\n" + " <leaf name=\"leaf29\">\n" + " <type name=\"instance-identifier\"/>\n" + " </leaf>\n" + " <container name=\"must-deviations-container\">\n" + " <presence value=\"Allows deviations on the leaf\"/>\n" + " <leaf name=\"leaf30\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </container>\n" + " <leaf name=\"leaf23\">\n" + " <type name=\"empty\"/>\n" + " </leaf>\n" + " </container>\n" + " <container name=\"test-when\">\n" + " <leaf name=\"when-check\">\n" + " <type name=\"boolean\"/>\n" + " </leaf>\n" + " <leaf name=\"gated-data\">\n" + " <when condition=\"../when-check = 'true'\"/>\n" + " <type name=\"uint16\"/>\n" + " </leaf>\n" + " </container>\n" + " <augment target-node=\"/cont1\">\n" + " <leaf name=\"leaf17\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " </augment>\n" + " <rpc name=\"rpc1\">\n" + " <input>\n" + " <leaf name=\"leaf20\">\n" + " <type name=\"tdef1\"/>\n" + " </leaf>\n" + " </input>\n" + " <output>\n" + " <container name=\"cont2\">\n" + " <leaf name=\"leaf21\">\n" + " <type name=\"empty\"/>\n" + " </leaf>\n" + " </container>\n" + " </output>\n" + " </rpc>\n" + "</module>\n"); + + char *printed; + struct ly_out *out; + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out)); + + UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod); + assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YIN, 0, 0)); + assert_int_equal(strlen(ori_res), ly_out_printed(out)); + assert_string_equal(printed, ori_res); + + ly_out_free(out, NULL, 1); + free(orig); + free(ori_res); +} + +static LY_ERR +test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + *module_data = user_data; + *format = LYS_IN_YIN; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_print_submodule(void **state) +{ + struct lys_module *mod; + + const char *mod_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"a\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a_mod=\"urn:a\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:a\"/>\n" + " <prefix value=\"a_mod\"/>\n" + " <include module=\"a-sub\"/>\n" + "</module>\n"; + + char *submod_yin = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"a-sub\"\n" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\n" + " xmlns:a_mod=\"urn:a\"\n" + " xmlns:yt=\"urn:ietf:params:xml:ns:yang:ietf-yang-types\">\n" + " <yang-version value=\"1.1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_mod\"/>\n" + " </belongs-to>\n" + " <import module=\"ietf-yang-types\">\n" + " <prefix value=\"yt\"/>\n" + " <revision-date date=\"2013-07-15\"/>\n" + " </import>\n\n" + " <description>\n" + " <text>YANG types</text>\n" + " </description>\n" + " <reference>\n" + " <text>RFC reference</text>\n" + " </reference>\n" + "</submodule>\n"; + + char *printed; + struct ly_out *out; + + assert_int_equal(LY_SUCCESS, ly_out_new_memory(&printed, 0, &out)); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod_yin); + + UTEST_ADD_MODULE(mod_yin, LYS_IN_YIN, NULL, &mod); + assert_int_equal(LY_SUCCESS, lys_print_submodule(out, mod->parsed->includes[0].submodule, LYS_OUT_YIN, 0, 0)); + assert_int_equal(strlen(submod_yin), ly_out_printed(out)); + assert_string_equal(printed, submod_yin); + + ly_out_free(out, NULL, 1); +} + +/* helper function to simplify unit test of each element using parse_content function */ +LY_ERR +test_element_helper(void **state, const char *data, void *dest, const char **text, struct lysp_ext_instance **exts) +{ + const char *name, *prefix; + size_t name_len, prefix_len; + LY_ERR ret = LY_SUCCESS; + struct yin_subelement subelems[71] = { + {LY_STMT_ACTION, dest, 0}, + {LY_STMT_ANYDATA, dest, 0}, + {LY_STMT_ANYXML, dest, 0}, + {LY_STMT_ARGUMENT, dest, 0}, + {LY_STMT_AUGMENT, dest, 0}, + {LY_STMT_BASE, dest, 0}, + {LY_STMT_BELONGS_TO, dest, 0}, + {LY_STMT_BIT, dest, 0}, + {LY_STMT_CASE, dest, 0}, + {LY_STMT_CHOICE, dest, 0}, + {LY_STMT_CONFIG, dest, 0}, + {LY_STMT_CONTACT, dest, 0}, + {LY_STMT_CONTAINER, dest, 0}, + {LY_STMT_DEFAULT, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_DESCRIPTION, dest, 0}, + {LY_STMT_DEVIATE, dest, 0}, + {LY_STMT_DEVIATION, dest, 0}, + {LY_STMT_ENUM, dest, 0}, + {LY_STMT_ERROR_APP_TAG, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_ERROR_MESSAGE, dest, 0}, + {LY_STMT_EXTENSION, dest, 0}, + {LY_STMT_FEATURE, dest, 0}, + {LY_STMT_FRACTION_DIGITS, dest, 0}, + {LY_STMT_GROUPING, dest, 0}, + {LY_STMT_IDENTITY, dest, 0}, + {LY_STMT_IF_FEATURE, dest, 0}, + {LY_STMT_IMPORT, dest, 0}, + {LY_STMT_INCLUDE, dest, 0}, + {LY_STMT_INPUT, dest, 0}, + {LY_STMT_KEY, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_LEAF, dest, 0}, + {LY_STMT_LEAF_LIST, dest, 0}, + {LY_STMT_LENGTH, dest, 0}, + {LY_STMT_LIST, dest, 0}, + {LY_STMT_MANDATORY, dest, 0}, + {LY_STMT_MAX_ELEMENTS, dest, 0}, + {LY_STMT_MIN_ELEMENTS, dest, 0}, + {LY_STMT_MODIFIER, dest, 0}, + {LY_STMT_MODULE, dest, 0}, + {LY_STMT_MUST, dest, 0}, + {LY_STMT_NAMESPACE, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_NOTIFICATION, dest, 0}, + {LY_STMT_ORDERED_BY, dest, 0}, + {LY_STMT_ORGANIZATION, dest, 0}, + {LY_STMT_OUTPUT, dest, 0}, + {LY_STMT_PATH, dest, 0}, + {LY_STMT_PATTERN, dest, 0}, + {LY_STMT_POSITION, dest, 0}, + {LY_STMT_PREFIX, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_PRESENCE, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_RANGE, dest, 0}, + {LY_STMT_REFERENCE, dest, 0}, + {LY_STMT_REFINE, dest, 0}, + {LY_STMT_REQUIRE_INSTANCE, dest, 0}, + {LY_STMT_REVISION, dest, 0}, + {LY_STMT_REVISION_DATE, dest, 0}, + {LY_STMT_RPC, dest, 0}, + {LY_STMT_STATUS, dest, 0}, + {LY_STMT_SUBMODULE, dest, 0}, + {LY_STMT_TYPE, dest, 0}, + {LY_STMT_TYPEDEF, dest, 0}, + {LY_STMT_UNIQUE, dest, 0}, + {LY_STMT_UNITS, dest, YIN_SUBELEM_UNIQUE}, + {LY_STMT_USES, dest, 0}, + {LY_STMT_VALUE, dest, 0}, + {LY_STMT_WHEN, dest, 0}, + {LY_STMT_YANG_VERSION, dest, 0}, + {LY_STMT_YIN_ELEMENT, dest, 0}, + {LY_STMT_EXTENSION_INSTANCE, dest, 0}, + {LY_STMT_ARG_TEXT, dest, 0}, + {LY_STMT_ARG_VALUE, dest, 0} + }; + + YCTX->main_ctx = (struct lysp_ctx *)YCTX; + ly_in_new_memory(data, &UTEST_IN); + lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx); + prefix = YCTX->xmlctx->prefix; + prefix_len = YCTX->xmlctx->prefix_len; + name = YCTX->xmlctx->name; + name_len = YCTX->xmlctx->name_len; + lyxml_ctx_next(YCTX->xmlctx); + + ret = yin_parse_content(YCTX, subelems, 71, NULL, + yin_match_keyword(YCTX, name, name_len, prefix, prefix_len, LY_STMT_NONE), text, exts); + + /* free parser and input */ + lyxml_ctx_free(YCTX->xmlctx); + YCTX->xmlctx = NULL; + ly_in_free(UTEST_IN, 0); + UTEST_IN = NULL; + return ret; +} + +#define EXT_SUBELEM "<myext:c-define name=\"MY_MTU\" xmlns:myext=\"urn:example:extensions\"/>" + +static void +test_enum_elem(void **state) +{ + struct lysp_type type = {0}; + const char *data; + + data = ELEMENT_WRAPPER_START + "<enum name=\"enum-name\">\n" + " <if-feature name=\"feature\" />\n" + " <value value=\"55\" />\n" + " <status value=\"deprecated\" />\n" + " <description><text>desc...</text></description>\n" + " <reference><text>ref...</text></reference>\n" + " " EXT_SUBELEM "\n" + "</enum>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + uint16_t flags = LYS_STATUS_DEPRC | LYS_SET_VALUE; + + CHECK_LYSP_TYPE_ENUM(type.enums, "desc...", 1, flags, 1, "enum-name", "ref...", 55); + assert_string_equal(type.enums->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(type.enums->exts, LY_STMT_ENUM); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); + + data = ELEMENT_WRAPPER_START + "<enum name=\"enum-name\"></enum>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_string_equal(type.enums->name, "enum-name"); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); +} + +static void +test_bit_elem(void **state) +{ + struct lysp_type type = {0}; + const char *data; + + data = ELEMENT_WRAPPER_START + "<bit name=\"bit-name\">\n" + " <if-feature name=\"feature\" />\n" + " <position value=\"55\" />\n" + " <status value=\"deprecated\" />\n" + " <description><text>desc...</text></description>\n" + " <reference><text>ref...</text></reference>\n" + EXT_SUBELEM + "</bit>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + uint16_t flags = LYS_STATUS_DEPRC | LYS_SET_VALUE; + + CHECK_LYSP_TYPE_ENUM(type.bits, "desc...", 1, flags, 1, "bit-name", "ref...", 55); + assert_string_equal(type.bits->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(type.bits->exts, LY_STMT_BIT); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); + + data = ELEMENT_WRAPPER_START + "<bit name=\"bit-name\"> </bit>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(type.bits, NULL, 0, 0, 0, "bit-name", NULL, 0); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof type); +} + +static void +test_status_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + /* test invalid value */ + data = ELEMENT_WRAPPER_START "<status value=\"invalid\"></status>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"status\" element. " + "Valid values are \"current\", \"deprecated\" and \"obsolete\".", NULL, 1); +} + +static void +test_yin_element_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + data = ELEMENT_WRAPPER_START "<yin-element value=\"invalid\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"yin-element\" element. " + "Valid values are \"true\" and \"false\".", NULL, 1); +} + +static void +test_yangversion_elem(void **state) +{ + const char *data; + uint8_t version = 0; + + /* invalid value */ + data = ELEMENT_WRAPPER_START "<yang-version value=\"version\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &version, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"version\" of \"value\" attribute in \"yang-version\" element. " + "Valid values are \"1\" and \"1.1\".", NULL, 1); +} + +static void +test_argument_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + const char *arg; + struct yin_argument_meta arg_meta = {&flags, &arg}; + + /* min subelems */ + data = ELEMENT_WRAPPER_START + "<argument name=\"arg\">" + "</argument>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &arg_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(arg, "arg"); + assert_true(flags == 0); + lydict_remove(UTEST_LYCTX, arg); +} + +static void +test_belongsto_elem(void **state) +{ + const char *data; + struct lysp_submodule submod; + + lydict_insert(UTEST_LYCTX, "module-name", 0, &PARSER_CUR_PMOD(YCTX)->mod->name); + + data = ELEMENT_WRAPPER_START "<belongs-to module=\"module-name\"></belongs-to>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &submod, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"prefix\" of \"belongs-to\" element.", NULL, 1); +} + +static void +test_config_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + data = ELEMENT_WRAPPER_START "<config value=\"false\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_SUCCESS); + assert_true(flags & LYS_CONFIG_R); + flags = 0; + + data = ELEMENT_WRAPPER_START "<config value=\"invalid\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"config\" element. " + "Valid values are \"true\" and \"false\".", NULL, 1); +} + +static void +test_default_elem(void **state) +{ + const char *data; + struct lysp_qname val = {0}; + + data = ELEMENT_WRAPPER_START "<default/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of default element.", NULL, 1); +} + +static void +test_err_app_tag_elem(void **state) +{ + const char *data; + const char *val = NULL; + + data = ELEMENT_WRAPPER_START "<error-app-tag/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of error-app-tag element.", NULL, 1); +} + +static void +test_err_msg_elem(void **state) +{ + const char *data; + const char *val = NULL; + + data = ELEMENT_WRAPPER_START "<error-message></error-message>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"value\" of \"error-message\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<error-message invalid=\"text\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Unexpected attribute \"invalid\" of \"error-message\" element.", NULL, 1); +} + +static void +test_fracdigits_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* invalid values */ + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"-1\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-1\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"02\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"02\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"1p\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"1p\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"19\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"19\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<fraction-digits value=\"999999999999999999\"></fraction-digits>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"999999999999999999\" of \"value\" attribute in \"fraction-digits\" element.", NULL, 1); +} + +static void +test_iffeature_elem(void **state) +{ + const char *data; + const char **iffeatures = NULL; + + data = ELEMENT_WRAPPER_START "<if-feature/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &iffeatures, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute name of if-feature element.", NULL, 1); + LY_ARRAY_FREE(iffeatures); + iffeatures = NULL; +} + +static void +test_length_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<length value=\"length-str\">\n" + " <error-message><value>err-msg</value></error-message>\n" + " <error-app-tag value=\"err-app-tag\"/>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + EXT_SUBELEM + "</length>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.length, "length-str", "desc", + "err-app-tag", "err-msg", 1, "ref"); + assert_true(type.flags & LYS_SET_LENGTH); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.length->exts[0]), LY_STMT_LENGTH); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START + "<length value=\"length-str\">" + "</length>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.length, "length-str", NULL, + NULL, NULL, 0, NULL); + lysp_type_free(&fctx, &type); + assert_true(type.flags & LYS_SET_LENGTH); + memset(&type, 0, sizeof(type)); + + data = ELEMENT_WRAPPER_START "<length></length>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of length element.", NULL, 1); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_modifier_elem(void **state) +{ + const char *data; + const char *pat; + + assert_int_equal(LY_SUCCESS, lydict_insert(UTEST_LYCTX, "\006pattern", 8, &pat)); + data = ELEMENT_WRAPPER_START "<modifier value=\"invert\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &pat, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invert\" of \"value\" attribute in \"modifier\" element. " + "Only valid value is \"invert-match\".", NULL, 1); + lydict_remove(UTEST_LYCTX, pat); +} + +static void +test_namespace_elem(void **state) +{ + const char *data; + const char *ns; + + data = ELEMENT_WRAPPER_START "<namespace/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &ns, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute uri of namespace element.", NULL, 1); +} + +static void +test_pattern_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<pattern value=\"super_pattern\">\n" + " <modifier value=\"invert-match\"/>\n" + " <error-message><value>err-msg-value</value></error-message>\n" + " <error-app-tag value=\"err-app-tag-value\"/>\n" + " <description><text>"pattern-desc"</text></description>\n" + " <reference><text>pattern-ref</text></reference>\n" + EXT_SUBELEM + "</pattern>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_true(type.flags & LYS_SET_PATTERN); + CHECK_LYSP_RESTR(type.patterns, "\x015super_pattern", "\"pattern-desc\"", + "err-app-tag-value", "err-msg-value", 1, "pattern-ref"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.patterns->exts[0]), LY_STMT_PATTERN); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<pattern value=\"pattern\"> </pattern>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.patterns, "\x006pattern", NULL, NULL, NULL, 0, NULL); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_value_position_elem(void **state) +{ + const char *data; + struct lysp_type_enum en = {0}; + + /* valid values */ + data = ELEMENT_WRAPPER_START "<value value=\"-55\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, -55); + memset(&en, 0, sizeof(en)); + + data = ELEMENT_WRAPPER_START "<value value=\"0\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0); + memset(&en, 0, sizeof(en)); + + data = ELEMENT_WRAPPER_START "<value value=\"-0\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0); + memset(&en, 0, sizeof(en)); + + /* valid positions */ + data = ELEMENT_WRAPPER_START "<position value=\"0\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_TYPE_ENUM(&(en), NULL, 0, LYS_SET_VALUE, 0, NULL, NULL, 0); + memset(&en, 0, sizeof(en)); + + /* invalid values */ + data = ELEMENT_WRAPPER_START "<value value=\"99999999999999999999999\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"99999999999999999999999\" of \"value\" attribute in \"value\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<value value=\"1k\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"1k\" of \"value\" attribute in \"value\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<value value=\"\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"value\" element.", NULL, 1); + + /*invalid positions */ + data = ELEMENT_WRAPPER_START "<position value=\"-5\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-5\" of \"value\" attribute in \"position\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<position value=\"-0\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-0\" of \"value\" attribute in \"position\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<position value=\"99999999999999999999\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"99999999999999999999\" of \"value\" attribute in \"position\" element.", NULL, 1); + + data = ELEMENT_WRAPPER_START "<position value=\"\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &en, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"position\" element.", NULL, 1); +} + +static void +test_prefix_elem(void **state) +{ + const char *data; + const char *value = NULL; + + data = ELEMENT_WRAPPER_START "<prefix value=\"pref\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &value, NULL, NULL), LY_SUCCESS); + assert_string_equal(value, "pref"); + lydict_remove(UTEST_LYCTX, value); +} + +static void +test_range_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<range value=\"range-str\">\n" + " <error-message><value>err-msg</value></error-message>\n" + " <error-app-tag value=\"err-app-tag\" />\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + EXT_SUBELEM + "</range>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.range, "range-str", "desc", + "err-app-tag", "err-msg", 1, "ref"); + assert_true(type.flags & LYS_SET_RANGE); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.range->exts[0]), LY_STMT_RANGE); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<range value=\"range-str\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_RESTR(type.range, "range-str", NULL, + NULL, NULL, 0, NULL); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_reqinstance_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + data = ELEMENT_WRAPPER_START "<require-instance value=\"true\">" EXT_SUBELEM "</require-instance>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_int_equal(type.require_instance, 1); + assert_true(type.flags & LYS_SET_REQINST); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.exts[0]), LY_STMT_REQUIRE_INSTANCE); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + data = ELEMENT_WRAPPER_START "<require-instance value=\"false\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_int_equal(type.require_instance, 0); + assert_true(type.flags & LYS_SET_REQINST); + memset(&type, 0, sizeof(type)); + + data = ELEMENT_WRAPPER_START "<require-instance value=\"invalid\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_EVALID); + memset(&type, 0, sizeof(type)); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"require-instance\" element. " + "Valid values are \"true\" and \"false\".", NULL, 1); +} + +static void +test_revision_date_elem(void **state) +{ + const char *data; + char rev[LY_REV_SIZE]; + + data = ELEMENT_WRAPPER_START "<revision-date date=\"2000-01-01\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, rev, NULL, NULL), LY_SUCCESS); + assert_string_equal(rev, "2000-01-01"); + + data = ELEMENT_WRAPPER_START "<revision-date date=\"2000-50-05\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, rev, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"2000-50-05\" of \"revision-date\".", NULL, 1); +} + +static void +test_unique_elem(void **state) +{ + const char *data; + const char **values = NULL; + + data = ELEMENT_WRAPPER_START "<unique tag=\"tag\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &values, NULL, NULL), LY_SUCCESS); + assert_string_equal(*values, "tag"); + lydict_remove(UTEST_LYCTX, *values); + LY_ARRAY_FREE(values); + values = NULL; +} + +static void +test_units_elem(void **state) +{ + const char *data; + const char *values = NULL; + + data = ELEMENT_WRAPPER_START "<units name=\"name\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &values, NULL, NULL), LY_SUCCESS); + assert_string_equal(values, "name"); + lydict_remove(UTEST_LYCTX, values); + values = NULL; +} + +static void +test_yin_text_value_elem(void **state) +{ + const char *data; + const char *val; + + data = ELEMENT_WRAPPER_START "<text>text</text>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "text"); + lydict_remove(UTEST_LYCTX, val); + + data = "<error-message xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <value>text</value> </error-message>"; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "text"); + lydict_remove(UTEST_LYCTX, val); + + data = ELEMENT_WRAPPER_START "<text></text>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal("", val); + lydict_remove(UTEST_LYCTX, val); +} + +static void +test_type_elem(void **state) +{ + const char *data; + struct lysp_type type = {0}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<type name=\"type-name\">\n" + " <base name=\"base-name\"/>\n" + " <bit name=\"bit\"/>\n" + " <enum name=\"enum\"/>\n" + " <fraction-digits value=\"2\"/>\n" + " <length value=\"length\"/>\n" + " <path value=\"/path\"/>\n" + " <pattern value=\"pattern\"/>\n" + " <range value=\"range\" />\n" + " <require-instance value=\"true\"/>\n" + " <type name=\"sub-type-name\"/>\n" + EXT_SUBELEM + "</type>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + assert_string_equal(type.name, "type-name"); + assert_string_equal(*type.bases, "base-name"); + assert_string_equal(type.bits->name, "bit"); + assert_string_equal(type.enums->name, "enum"); + assert_int_equal(type.fraction_digits, 2); + CHECK_LYSP_RESTR(type.length, "length", NULL, + NULL, NULL, 0, NULL); + assert_string_equal(type.path->expr, "/path"); + CHECK_LYSP_RESTR(type.patterns, "\006pattern", NULL, + NULL, NULL, 0, NULL); + CHECK_LYSP_RESTR(type.range, "range", NULL, + NULL, NULL, 0, NULL); + assert_int_equal(type.require_instance, 1); + assert_string_equal(type.types->name, "sub-type-name"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(type.exts[0]), LY_STMT_TYPE); + assert_true(type.flags & LYS_SET_BASE); + assert_true(type.flags & LYS_SET_BIT); + assert_true(type.flags & LYS_SET_ENUM); + assert_true(type.flags & LYS_SET_FRDIGITS); + assert_true(type.flags & LYS_SET_LENGTH); + assert_true(type.flags & LYS_SET_PATH); + assert_true(type.flags & LYS_SET_PATTERN); + assert_true(type.flags & LYS_SET_RANGE); + assert_true(type.flags & LYS_SET_REQINST); + assert_true(type.flags & LYS_SET_TYPE); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<type name=\"type-name\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &type, NULL, NULL), LY_SUCCESS); + lysp_type_free(&fctx, &type); + memset(&type, 0, sizeof(type)); +} + +static void +test_max_elems_elem(void **state) +{ + const char *data; + struct lysp_node_list list = {0}; + struct lysp_refine refine = {0}; + + data = "<refine xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"10\"/> </refine>"; + assert_int_equal(test_element_helper(state, data, &refine, NULL, NULL), LY_SUCCESS); + assert_int_equal(refine.max, 10); + assert_true(refine.flags & LYS_SET_MAX); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"0\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"0\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"-10\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"-10\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"k\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"k\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); + + data = "<list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <max-elements value=\"u12\"/> </list>"; + assert_int_equal(test_element_helper(state, data, &list, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"u12\" of \"value\" attribute in \"max-elements\" element.", NULL, 1); +} + +static void +test_min_elems_elem(void **state) +{ + const char *data; + struct lysp_node_leaflist llist = {0}; + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"-5\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Value \"-5\" of \"value\" attribute in \"min-elements\" element is out of bounds.", NULL, 1); + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"99999999999999999\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Value \"99999999999999999\" of \"value\" attribute in \"min-elements\" element is out of bounds.", NULL, 1); + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"5k\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"5k\" of \"value\" attribute in \"min-elements\" element.", NULL, 1); + + data = "<leaf-list xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"> <min-elements value=\"05\"/> </leaf-list>"; + assert_int_equal(test_element_helper(state, data, &llist, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"05\" of \"value\" attribute in \"min-elements\" element.", NULL, 1); +} + +static void +test_ordby_elem(void **state) +{ + const char *data; + uint16_t flags = 0; + + data = ELEMENT_WRAPPER_START "<ordered-by value=\"user\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_SUCCESS); + assert_true(flags & LYS_ORDBY_USER); + + data = ELEMENT_WRAPPER_START "<ordered-by value=\"inv\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &flags, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"inv\" of \"value\" attribute in \"ordered-by\" element. " + "Valid values are \"system\" and \"user\".", NULL, 1); +} + +static void +test_any_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings}; + struct lysp_node_anydata *parsed = NULL; + uint16_t flags; + + /* anyxml max subelems */ + data = ELEMENT_WRAPPER_START + "<anyxml name=\"any-name\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\" />\n" + " <mandatory value=\"true\" />\n" + " <must condition=\"must-cond\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</anyxml>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_anydata *)siblings; + flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "any-name", 0, LYS_ANYXML, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_ANYXML); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* anydata max subelems */ + data = ELEMENT_WRAPPER_START + "<anydata name=\"any-name\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\" />\n" + " <mandatory value=\"true\" />\n" + " <must condition=\"must-cond\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</anydata>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_anydata *)siblings; + flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "any-name", 0, LYS_ANYDATA, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_ANYDATA); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + node_meta.parent = (void *)0x10; + data = ELEMENT_WRAPPER_START "<anydata name=\"any-name\"> </anydata>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_anydata *)siblings; + assert_ptr_equal(parsed->parent, node_meta.parent); + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "any-name", 0, LYS_ANYDATA, 1, NULL, 0); + lysp_node_free(&fctx, siblings); +} + +static void +test_leaf_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings}; + struct lysp_node_leaf *parsed = NULL; + uint16_t flags; + + /* max elements */ + data = ELEMENT_WRAPPER_START + "<leaf name=\"leaf\">\n" + " <config value=\"true\" />\n" + " <default value=\"def-val\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\" />\n" + " <mandatory value=\"true\" />\n" + " <must condition=\"must-cond\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</leaf>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaf *)siblings; + flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_DEPRC; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "leaf", 0, LYS_LEAF, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF); + assert_string_equal(parsed->musts->arg.str, "must-cond"); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + assert_string_equal(parsed->dflt.str, "def-val"); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min elements */ + data = ELEMENT_WRAPPER_START "<leaf name=\"leaf\"> <type name=\"type\"/> </leaf>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaf *)siblings; + assert_string_equal(parsed->name, "leaf"); + assert_string_equal(parsed->type.name, "type"); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_leaf_list_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {.parent = NULL, .nodes = &siblings}; + struct lysp_node_leaflist *parsed = NULL; + uint16_t flags; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <config value=\"true\" />\n" + " <default value=\"def-val0\"/>\n" + " <default value=\"def-val1\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\"/>\n" + " <max-elements value=\"5\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <ordered-by value=\"user\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MAX; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "llist", 0, LYS_LEAFLIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + assert_string_equal(parsed->dflts[0].str, "def-val0"); + assert_string_equal(parsed->dflts[1].str, "def-val1"); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_int_equal(parsed->max, 5); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF_LIST); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\"/>\n" + " <min-elements value=\"5\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <ordered-by value=\"user\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MIN; + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "llist", 0, LYS_LEAFLIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_int_equal(parsed->min, 5); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LEAF_LIST); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <config value=\"true\" />\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"feature\"/>\n" + " <max-elements value=\"15\"/>\n" + " <min-elements value=\"5\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <ordered-by value=\"user\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <type name=\"type\"/>\n" + " <units name=\"uni\"/>\n" + " <when condition=\"when-cond\"/>\n" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + flags = LYS_CONFIG_W | LYS_ORDBY_USER | LYS_STATUS_CURR | LYS_SET_MIN | LYS_SET_MAX; + CHECK_LYSP_NODE(parsed, "desc", 0, flags, 1, + "llist", 0, LYS_LEAFLIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_int_equal(parsed->min, 5); + assert_int_equal(parsed->max, 15); + assert_string_equal(parsed->type.name, "type"); + assert_string_equal(parsed->units, "uni"); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <type name=\"type\"/>\n" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_leaflist *)siblings; + assert_string_equal(parsed->name, "llist"); + assert_string_equal(parsed->type.name, "type"); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* invalid combinations */ + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <max-elements value=\"5\"/>\n" + " <min-elements value=\"15\"/>\n" + " <type name=\"type\"/>" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid combination of min-elements and max-elements: min value 15 is bigger than the max value 5.", + NULL, 4); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">\n" + " <default value=\"def-val1\"/>\n" + " <min-elements value=\"15\"/>\n" + " <type name=\"type\"/>\n" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid combination of sub-elemnts \"min-elements\" and \"default\" in \"leaf-list\" element.", NULL, 5); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + data = ELEMENT_WRAPPER_START + "<leaf-list name=\"llist\">" + "</leaf-list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"type\" of \"leaf-list\" element.", NULL, 1); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_presence_elem(void **state) +{ + const char *data; + const char *val; + + data = ELEMENT_WRAPPER_START "<presence value=\"presence-val\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "presence-val"); + lydict_remove(UTEST_LYCTX, val); + + data = ELEMENT_WRAPPER_START "<presence/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of presence element.", NULL, 1); +} + +static void +test_key_elem(void **state) +{ + const char *data; + const char *val; + + data = ELEMENT_WRAPPER_START "<key value=\"key-value\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_SUCCESS); + assert_string_equal(val, "key-value"); + lydict_remove(UTEST_LYCTX, val); + + data = ELEMENT_WRAPPER_START "<key/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &val, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory attribute value of key element.", NULL, 1); +} + +static void +test_uses_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_uses *parsed = NULL; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<uses name=\"uses-name\">\n" + " <when condition=\"cond\" />\n" + " <if-feature name=\"feature\" />\n" + " <status value=\"obsolete\" />\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <refine target-node=\"target\"/>\n" + " <augment target-node=\"target\" />\n" + EXT_SUBELEM + "</uses>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_uses *)&siblings[0]; + CHECK_LYSP_NODE(parsed, "desc", 1, LYS_STATUS_OBSLT, 1, + "uses-name", 0, LYS_USES, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "feature"); + assert_string_equal(parsed->refines->nodeid, "target"); + assert_string_equal(parsed->augments->nodeid, "target"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_USES); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<uses name=\"uses-name\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(siblings[0].name, "uses-name"); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_list_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_list *parsed = NULL; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<list name=\"list-name\">\n" + " <when condition=\"when\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <must condition=\"must-cond\"/>\n" + " <key value=\"key\"/>\n" + " <unique tag=\"utag\"/>\n" + " <config value=\"true\"/>\n" + " <min-elements value=\"10\"/>\n" + " <ordered-by value=\"user\"/>\n" + " <status value=\"deprecated\"/>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <container name=\"cont\"/>\n" + " <choice name=\"choice\"/>\n" + " <action name=\"action\"/>\n" + " <grouping name=\"grp\"/>\n" + " <notification name=\"notf\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + EXT_SUBELEM + "</list>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_list *)&siblings[0]; + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "cont"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(parsed->child->next->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "sub-list"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + uint16_t flags = LYS_ORDBY_USER | LYS_STATUS_DEPRC | LYS_CONFIG_W | LYS_SET_MIN; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "list-name", 0, LYS_LIST, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "must-cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when", NULL, 0, NULL); + assert_string_equal(parsed->groupings->name, "grp"); + assert_string_equal(parsed->actions->name, "action"); + assert_int_equal(parsed->groupings->nodetype, LYS_GROUPING); + assert_string_equal(parsed->notifs->name, "notf"); + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->key, "key"); + assert_int_equal(parsed->min, 10); + assert_string_equal(parsed->typedefs->name, "tpdf"); + assert_string_equal(parsed->uniques->str, "utag"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_LIST); + lysp_node_free(&fctx, siblings); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<list name=\"list-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_list *)&siblings[0]; + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "list-name", 0, LYS_LIST, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_notification_elem(void **state) +{ + const char *data; + struct lysp_node_notif *notifs = NULL; + struct tree_node_meta notif_meta = {NULL, (struct lysp_node **)¬ifs}; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<notification name=\"notif-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <must condition=\"cond\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + " <container name=\"cont\"/>\n" + " <choice name=\"choice\"/>\n" + " <grouping name=\"grp\"/>\n" + EXT_SUBELEM + "</notification>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, ¬if_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(notifs->name, "notif-name"); + assert_string_equal(notifs->child->name, "anyd"); + assert_int_equal(notifs->child->nodetype, LYS_ANYDATA); + assert_string_equal(notifs->child->next->name, "anyx"); + assert_int_equal(notifs->child->next->nodetype, LYS_ANYXML); + assert_string_equal(notifs->child->next->next->name, "leaf"); + assert_int_equal(notifs->child->next->next->nodetype, LYS_LEAF); + assert_string_equal(notifs->child->next->next->next->name, "llist"); + assert_int_equal(notifs->child->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(notifs->child->next->next->next->next->name, "sub-list"); + assert_int_equal(notifs->child->next->next->next->next->nodetype, LYS_LIST); + assert_true(notifs->flags & LYS_STATUS_DEPRC); + assert_string_equal(notifs->groupings->name, "grp"); + assert_int_equal(notifs->groupings->nodetype, LYS_GROUPING); + assert_string_equal(notifs->child->next->next->next->next->next->name, "uses-name"); + assert_int_equal(notifs->child->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(notifs->child->next->next->next->next->next->next->name, "cont"); + assert_int_equal(notifs->child->next->next->next->next->next->next->nodetype, LYS_CONTAINER); + assert_int_equal(notifs->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(notifs->child->next->next->next->next->next->next->next->name, "choice"); + assert_null(notifs->child->next->next->next->next->next->next->next->next); + assert_string_equal(notifs->iffeatures[0].str, "iff"); + assert_string_equal(notifs->musts->arg.str, "cond"); + assert_int_equal(notifs->nodetype, LYS_NOTIF); + assert_null(notifs->parent); + assert_string_equal(notifs->ref, "ref"); + assert_string_equal(notifs->typedefs->name, "tpdf"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(notifs->exts[0]), LY_STMT_NOTIFICATION); + lysp_node_free(&fctx, (struct lysp_node *)notifs); + notifs = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<notification name=\"notif-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, ¬if_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(notifs->name, "notif-name"); + lysp_node_free(&fctx, (struct lysp_node *)notifs); + notifs = NULL; +} + +static void +test_grouping_elem(void **state) +{ + const char *data; + struct lysp_node_grp *grps = NULL; + struct tree_node_meta grp_meta = {NULL, (struct lysp_node **)&grps}; + + /* max subelems */ + data = ELEMENT_WRAPPER_START + "<grouping name=\"grp-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"sub-grp\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <notification name=\"notf\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + " <action name=\"act\"/>\n" + " <container name=\"cont\"/>\n" + " <choice name=\"choice\"/>\n" + EXT_SUBELEM + "</grouping>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &grp_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(grps->name, "grp-name"); + assert_string_equal(grps->child->name, "anyd"); + assert_string_equal(grps->child->next->name, "anyx"); + assert_string_equal(grps->child->next->next->name, "leaf"); + assert_string_equal(grps->child->next->next->next->name, "llist"); + assert_string_equal(grps->child->next->next->next->next->name, "list"); + assert_string_equal(grps->dsc, "desc"); + assert_true(grps->flags & LYS_STATUS_CURR); + assert_string_equal(grps->groupings->name, "sub-grp"); + assert_int_equal(grps->nodetype, LYS_GROUPING); + assert_string_equal(grps->notifs->name, "notf"); + assert_null(grps->parent); + assert_string_equal(grps->ref, "ref"); + assert_string_equal(grps->typedefs->name, "tpdf"); + assert_string_equal(grps->actions->name, "act"); + assert_string_equal(grps->child->next->next->next->next->next->name, "uses-name"); + assert_int_equal(grps->child->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(grps->child->next->next->next->next->next->next->name, "cont"); + assert_int_equal(grps->child->next->next->next->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(grps->child->next->next->next->next->next->next->next->name, "choice"); + assert_int_equal(grps->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(grps->exts[0]), LY_STMT_GROUPING); + lysp_node_free(&fctx, &grps->node); + grps = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<grouping name=\"grp-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &grp_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(grps->name, "grp-name"); + lysp_node_free(&fctx, &grps->node); + grps = NULL; +} + +static void +test_container_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_container *parsed = NULL; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<container name=\"cont-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <config value=\"true\"/>\n" + " <container name=\"subcont\"/>\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"sub-grp\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <must condition=\"cond\"/>\n" + " <notification name=\"notf\"/>\n" + " <presence value=\"presence\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + " <when condition=\"when-cond\"/>\n" + " <action name=\"act\"/>\n" + " <choice name=\"choice\"/>\n" + EXT_SUBELEM + "</container>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_container *)siblings; + uint16_t flags = LYS_CONFIG_W | LYS_STATUS_CURR; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "cont-name", 0, LYS_CONTAINER, 0, "ref", 1); + CHECK_LYSP_RESTR(parsed->musts, "cond", NULL, NULL, NULL, 0, NULL); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->presence, "presence"); + assert_string_equal(parsed->typedefs->name, "tpdf"); + assert_string_equal(parsed->groupings->name, "sub-grp"); + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "subcont"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->name, "list"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + assert_string_equal(parsed->notifs->name, "notf"); + assert_string_equal(parsed->actions->name, "act"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CONTAINER); + lysp_node_free(&fctx, siblings); + ly_set_erase(&YCTX->tpdfs_nodes, NULL); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<container name=\"cont-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_container *)siblings; + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "cont-name", 0, LYS_CONTAINER, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_case_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_case *parsed = NULL; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<case name=\"case-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <container name=\"subcont\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <uses name=\"uses-name\"/>\n" + " <when condition=\"when-cond\"/>\n" + " <choice name=\"choice\"/>\n" + EXT_SUBELEM + "</case>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_case *)siblings; + uint16_t flags = LYS_STATUS_CURR; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "case-name", 0, LYS_CASE, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "subcont"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->name, "list"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_USES); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_CHOICE); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CASE); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<case name=\"case-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_case *)siblings; + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "case-name", 0, LYS_CASE, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_choice_elem(void **state) +{ + const char *data; + struct lysp_node *siblings = NULL; + struct tree_node_meta node_meta = {NULL, &siblings}; + struct lysp_node_choice *parsed = NULL; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<choice name=\"choice-name\">\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <case name=\"sub-case\"/>\n" + " <choice name=\"choice\"/>\n" + " <config value=\"true\"/>\n" + " <container name=\"subcont\"/>\n" + " <default value=\"def\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <mandatory value=\"true\" />\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</choice>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_choice *)siblings; + uint16_t flags = LYS_CONFIG_W | LYS_MAND_TRUE | LYS_STATUS_CURR; + + CHECK_LYSP_NODE(parsed, "desc", 1, flags, 1, + "choice-name", 0, LYS_CHOICE, 0, "ref", 1); + CHECK_LYSP_WHEN(parsed->when, "when-cond", NULL, 0, NULL); + assert_string_equal(parsed->iffeatures[0].str, "iff"); + assert_string_equal(parsed->child->name, "anyd"); + assert_int_equal(parsed->child->nodetype, LYS_ANYDATA); + assert_string_equal(parsed->child->next->name, "anyx"); + assert_int_equal(parsed->child->next->nodetype, LYS_ANYXML); + assert_string_equal(parsed->child->next->next->name, "sub-case"); + assert_int_equal(parsed->child->next->next->nodetype, LYS_CASE); + assert_string_equal(parsed->child->next->next->next->name, "choice"); + assert_int_equal(parsed->child->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(parsed->child->next->next->next->next->name, "subcont"); + assert_int_equal(parsed->child->next->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(parsed->child->next->next->next->next->next->name, "leaf"); + assert_int_equal(parsed->child->next->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(parsed->child->next->next->next->next->next->next->name, "llist"); + assert_int_equal(parsed->child->next->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(parsed->child->next->next->next->next->next->next->next->name, "list"); + assert_int_equal(parsed->child->next->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_null(parsed->child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(parsed->exts[0]), LY_STMT_CHOICE); + lysp_node_free(&fctx, siblings); + siblings = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<choice name=\"choice-name\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &node_meta, NULL, NULL), LY_SUCCESS); + parsed = (struct lysp_node_choice *)siblings; + assert_string_equal(parsed->name, "choice-name"); + CHECK_LYSP_NODE(parsed, NULL, 0, 0, 0, + "choice-name", 0, LYS_CHOICE, 0, NULL, 0); + lysp_node_free(&fctx, siblings); + siblings = NULL; +} + +static void +test_inout_elem(void **state) +{ + const char *data; + struct lysp_node_action_inout inout = {0}; + struct inout_meta inout_meta = {NULL, &inout}; + + /* max subelements */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<input>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"subcont\"/>\n" + " <grouping name=\"sub-grp\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <must condition=\"cond\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + EXT_SUBELEM + "</input>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_ACTION_INOUT(&(inout), 1, 1, 1, 1, LYS_INPUT, 0, 1); + CHECK_LYSP_RESTR(inout.musts, "cond", NULL, NULL, NULL, 0, NULL); + assert_string_equal(inout.typedefs->name, "tpdf"); + assert_string_equal(inout.groupings->name, "sub-grp"); + assert_string_equal(inout.child->name, "anyd"); + assert_int_equal(inout.child->nodetype, LYS_ANYDATA); + assert_string_equal(inout.child->next->name, "anyx"); + assert_int_equal(inout.child->next->nodetype, LYS_ANYXML); + assert_string_equal(inout.child->next->next->name, "choice"); + assert_int_equal(inout.child->next->next->nodetype, LYS_CHOICE); + assert_string_equal(inout.child->next->next->next->name, "subcont"); + assert_int_equal(inout.child->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(inout.child->next->next->next->next->name, "leaf"); + assert_int_equal(inout.child->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(inout.child->next->next->next->next->next->name, "llist"); + assert_int_equal(inout.child->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(inout.child->next->next->next->next->next->next->name, "list"); + assert_int_equal(inout.child->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(inout.child->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(inout.child->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(inout.child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(inout.exts[0]), LY_STMT_INPUT); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + /* max subelements */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<output>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"subcont\"/>\n" + " <grouping name=\"sub-grp\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <must condition=\"cond\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + " <uses name=\"uses-name\"/>\n" + EXT_SUBELEM + "</output>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + CHECK_LYSP_ACTION_INOUT(&(inout), 1, 1, 1, 1, LYS_OUTPUT, 0, 1); + assert_string_equal(inout.musts->arg.str, "cond"); + assert_string_equal(inout.typedefs->name, "tpdf"); + assert_string_equal(inout.groupings->name, "sub-grp"); + assert_string_equal(inout.child->name, "anyd"); + assert_int_equal(inout.child->nodetype, LYS_ANYDATA); + assert_string_equal(inout.child->next->name, "anyx"); + assert_int_equal(inout.child->next->nodetype, LYS_ANYXML); + assert_string_equal(inout.child->next->next->name, "choice"); + assert_int_equal(inout.child->next->next->nodetype, LYS_CHOICE); + assert_string_equal(inout.child->next->next->next->name, "subcont"); + assert_int_equal(inout.child->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(inout.child->next->next->next->next->name, "leaf"); + assert_int_equal(inout.child->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(inout.child->next->next->next->next->next->name, "llist"); + assert_int_equal(inout.child->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(inout.child->next->next->next->next->next->next->name, "list"); + assert_int_equal(inout.child->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(inout.child->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(inout.child->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(inout.child->next->next->next->next->next->next->next->next); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(inout.exts[0]), LY_STMT_OUTPUT); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<input><leaf name=\"l\"><type name=\"empty\"/></leaf></input>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + data = ELEMENT_WRAPPER_START "<output><leaf name=\"l\"><type name=\"empty\"/></leaf></output>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_SUCCESS); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + memset(&inout, 0, sizeof inout); + + /* invalid combinations */ + data = ELEMENT_WRAPPER_START "<input name=\"test\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &inout_meta, NULL, NULL), LY_EVALID); + lysp_node_free(&fctx, (struct lysp_node *)&inout); + CHECK_LOG_CTX("Unexpected attribute \"name\" of \"input\" element.", NULL, 1); + memset(&inout, 0, sizeof inout); +} + +static void +test_action_elem(void **state) +{ + const char *data; + struct lysp_node_action *actions = NULL; + struct tree_node_meta act_meta = {NULL, (struct lysp_node **)&actions}; + uint16_t flags; + + /* max subelems */ + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<action name=\"act\">\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"grouping\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <input><uses name=\"uses-name\"/></input>\n" + " <output><must condition=\"cond\"/><leaf name=\"l\"><type name=\"type\"/></leaf></output>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM + "</action>" + ELEMENT_WRAPPER_END; + /* there must be parent for action */ + act_meta.parent = (void *)1; + assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS); + act_meta.parent = NULL; + flags = LYS_STATUS_DEPRC; + CHECK_LYSP_ACTION(actions, "desc", 1, flags, 1, 1,\ + 1, 0, 0, 0,\ + 1, 0,\ + "act", LYS_ACTION, \ + 1, 0, 0, 1,\ + 1, 0,\ + 1, "ref", 1); + + assert_string_equal(actions->iffeatures[0].str, "iff"); + assert_string_equal(actions->typedefs->name, "tpdf"); + assert_string_equal(actions->groupings->name, "grouping"); + assert_string_equal(actions->output.musts->arg.str, "cond"); + assert_string_equal(actions->input.child->name, "uses-name"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(actions->exts[0]), LY_STMT_ACTION); + lysp_node_free(&fctx, (struct lysp_node *)actions); + actions = NULL; + + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<rpc name=\"act\">\n" + " <description><text>desc</text></description>\n" + " <grouping name=\"grouping\"/>\n" + " <if-feature name=\"iff\"/>\n" + " <input><uses name=\"uses-name\"/></input>\n" + " <output><must condition=\"cond\"/><leaf name=\"l\"><type name=\"type\"/></leaf></output>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"deprecated\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM + "</rpc>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS); + flags = LYS_STATUS_DEPRC; + CHECK_LYSP_ACTION(actions, "desc", 1, flags, 1, 1,\ + 1, 0, 0, 0,\ + 1, 0,\ + "act", LYS_RPC, \ + 1, 0, 0, 1,\ + 1, 0,\ + 0, "ref", 1); + + assert_string_equal(actions->iffeatures[0].str, "iff"); + assert_string_equal(actions->typedefs->name, "tpdf"); + assert_string_equal(actions->groupings->name, "grouping"); + assert_string_equal(actions->input.child->name, "uses-name"); + assert_string_equal(actions->output.musts->arg.str, "cond"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(actions->exts[0]), LY_STMT_RPC); + lysp_node_free(&fctx, (struct lysp_node *)actions); + actions = NULL; + + /* min subelems */ + data = ELEMENT_WRAPPER_START "<action name=\"act\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &act_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(actions->name, "act"); + lysp_node_free(&fctx, (struct lysp_node *)actions); + actions = NULL; +} + +static void +test_augment_elem(void **state) +{ + const char *data; + struct lysp_node_augment *augments = NULL; + struct tree_node_meta aug_meta = {NULL, (struct lysp_node **)&augments}; + + PARSER_CUR_PMOD(YCTX)->version = LYS_VERSION_1_1; + data = ELEMENT_WRAPPER_START + "<augment target-node=\"target\">\n" + " <action name=\"action\"/>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <case name=\"case\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"subcont\"/>\n" + " <description><text>desc</text></description>\n" + " <if-feature name=\"iff\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"list\"/>\n" + " <notification name=\"notif\"/>\n" + " <reference><text>ref</text></reference>\n" + " <status value=\"current\"/>\n" + " <uses name=\"uses\"/>\n" + " <when condition=\"when-cond\"/>\n" + EXT_SUBELEM + "</augment>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &aug_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(augments->nodeid, "target"); + assert_null(augments->parent); + assert_int_equal(augments->nodetype, LYS_AUGMENT); + assert_true(augments->flags & LYS_STATUS_CURR); + assert_string_equal(augments->dsc, "desc"); + assert_string_equal(augments->ref, "ref"); + assert_string_equal(augments->when->cond, "when-cond"); + assert_string_equal(augments->iffeatures[0].str, "iff"); + assert_string_equal(augments->child->name, "anyd"); + assert_int_equal(augments->child->nodetype, LYS_ANYDATA); + assert_string_equal(augments->child->next->name, "anyx"); + assert_int_equal(augments->child->next->nodetype, LYS_ANYXML); + assert_string_equal(augments->child->next->next->name, "case"); + assert_int_equal(augments->child->next->next->nodetype, LYS_CASE); + assert_string_equal(augments->child->next->next->next->name, "choice"); + assert_int_equal(augments->child->next->next->next->nodetype, LYS_CHOICE); + assert_string_equal(augments->child->next->next->next->next->name, "subcont"); + assert_int_equal(augments->child->next->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(augments->child->next->next->next->next->next->name, "leaf"); + assert_int_equal(augments->child->next->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(augments->child->next->next->next->next->next->next->name, "llist"); + assert_int_equal(augments->child->next->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(augments->child->next->next->next->next->next->next->next->name, "list"); + assert_int_equal(augments->child->next->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(augments->child->next->next->next->next->next->next->next->next->name, "uses"); + assert_int_equal(augments->child->next->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(augments->child->next->next->next->next->next->next->next->next->next); + assert_string_equal(augments->actions->name, "action"); + assert_string_equal(augments->notifs->name, "notif"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(augments->exts[0]), LY_STMT_AUGMENT); + lysp_node_free(&fctx, (struct lysp_node *)augments); + augments = NULL; + + data = ELEMENT_WRAPPER_START "<augment target-node=\"target\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &aug_meta, NULL, NULL), LY_SUCCESS); + assert_string_equal(augments->nodeid, "target"); + lysp_node_free(&fctx, (struct lysp_node *)augments); + augments = NULL; +} + +static void +test_deviate_elem(void **state) +{ + const char *data; + struct lysp_deviate *deviates = NULL; + + /* invalid arguments */ + data = ELEMENT_WRAPPER_START "<deviate value=\"\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START "<deviate value=\"invalid\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"invalid\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START "<deviate value=\"ad\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"ad\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START "<deviate value=\"adds\" />" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Invalid value \"adds\" of \"value\" attribute in \"deviate\" element. " + "Valid values are \"not-supported\", \"add\", \"replace\" and \"delete\".", NULL, 1); + deviates = NULL; + + data = ELEMENT_WRAPPER_START + "<deviate value=\"not-supported\">\n" + " <must condition=\"c\"/>\n" + "</deviate>" + ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviates, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Deviate of this type doesn't allow \"must\" as it's sub-element.", NULL, 2); +} + +static void +test_deviation_elem(void **state) +{ + const char *data; + struct lysp_deviation *deviations = NULL; + + /* invalid */ + data = ELEMENT_WRAPPER_START "<deviation target-node=\"target\"/>" ELEMENT_WRAPPER_END; + assert_int_equal(test_element_helper(state, data, &deviations, NULL, NULL), LY_EVALID); + CHECK_LOG_CTX("Missing mandatory sub-element \"deviate\" of \"deviation\" element.", NULL, 1); +} + +static struct lysp_module * +mod_renew(struct lysp_yin_ctx *ctx) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_module *pmod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + pmod = calloc(1, sizeof *pmod); + ctx->parsed_mods->objs[0] = pmod; + pmod->mod = calloc(1, sizeof *pmod->mod); + pmod->mod->parsed = pmod; + pmod->mod->ctx = ly_ctx; + + fctx.mod = pmod->mod; + + return pmod; +} + +static void +test_module_elem(void **state) +{ + const char *data; + struct lysp_module *lysp_mod = mod_renew(YCTX); + + /* max subelems */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"ns\"/>\n" + " <prefix value=\"pref\"/>\n" + " <include module=\"b-mod\"/>\n" + " <import module=\"a-mod\"><prefix value=\"imp-pref\"/></import>\n" + " <organization><text>org</text></organization>\n" + " <contact><text>contact</text></contact>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <revision date=\"2019-02-02\"/>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"cont\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <uses name=\"uses-name\"/>\n" + " <augment target-node=\"target\"/>\n" + " <deviation target-node=\"target\">\n" + " <deviate value=\"not-supported\"/>\n" + " </deviation>\n" + " <extension name=\"ext\"/>\n" + " <feature name=\"feature\"/>\n" + " <grouping name=\"grp\"/>\n" + " <identity name=\"ident-name\"/>\n" + " <notification name=\"notf\"/>\n" + " <rpc name=\"rpc-name\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM "\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + + assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_SUCCESS); + assert_string_equal(lysp_mod->mod->name, "mod"); + assert_string_equal(lysp_mod->revs[0].date, "2019-02-02"); + assert_string_equal(lysp_mod->mod->ns, "ns"); + assert_string_equal(lysp_mod->mod->prefix, "pref"); + assert_null(lysp_mod->mod->filepath); + assert_string_equal(lysp_mod->mod->org, "org"); + assert_string_equal(lysp_mod->mod->contact, "contact"); + assert_string_equal(lysp_mod->mod->dsc, "desc"); + assert_string_equal(lysp_mod->mod->ref, "ref"); + assert_int_equal(lysp_mod->version, LYS_VERSION_1_1); + CHECK_LYSP_IMPORT(lysp_mod->imports, NULL, 0, "a-mod", + "imp-pref", NULL, ""); + assert_string_equal(lysp_mod->includes->name, "b-mod"); + assert_string_equal(lysp_mod->extensions->name, "ext"); + assert_string_equal(lysp_mod->features->name, "feature"); + assert_string_equal(lysp_mod->identities->name, "ident-name"); + assert_string_equal(lysp_mod->typedefs->name, "tpdf"); + assert_string_equal(lysp_mod->groupings->name, "grp"); + assert_string_equal(lysp_mod->data->name, "anyd"); + assert_int_equal(lysp_mod->data->nodetype, LYS_ANYDATA); + assert_string_equal(lysp_mod->data->next->name, "anyx"); + assert_int_equal(lysp_mod->data->next->nodetype, LYS_ANYXML); + assert_string_equal(lysp_mod->data->next->next->name, "choice"); + assert_int_equal(lysp_mod->data->next->next->nodetype, LYS_CHOICE); + assert_string_equal(lysp_mod->data->next->next->next->name, "cont"); + assert_int_equal(lysp_mod->data->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(lysp_mod->data->next->next->next->next->name, "leaf"); + assert_int_equal(lysp_mod->data->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(lysp_mod->data->next->next->next->next->next->name, "llist"); + assert_int_equal(lysp_mod->data->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(lysp_mod->data->next->next->next->next->next->next->name, "sub-list"); + assert_int_equal(lysp_mod->data->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(lysp_mod->data->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(lysp_mod->data->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(lysp_mod->data->next->next->next->next->next->next->next->next); + assert_string_equal(lysp_mod->augments->nodeid, "target"); + assert_string_equal(lysp_mod->rpcs->name, "rpc-name"); + assert_string_equal(lysp_mod->notifs->name, "notf"); + assert_string_equal(lysp_mod->deviations->nodeid, "target"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(lysp_mod->exts[0]), LY_STMT_MODULE); + + /* min subelems */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_mod = mod_renew(YCTX); + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <namespace uri=\"ns\"/>\n" + " <prefix value=\"pref\"/>\n" + " <yang-version value=\"1.1\"/>\n" + "</module>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_SUCCESS); + assert_string_equal(lysp_mod->mod->name, "mod"); + + /* incorrect subelem order */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_mod = mod_renew(YCTX); + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <feature name=\"feature\"/>\n" + " <namespace uri=\"ns\"/>\n" + " <prefix value=\"pref\"/>\n" + " <yang-version value=\"1.1\"/>\n" + "</module>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_EVALID); + CHECK_LOG_CTX("Invalid order of module\'s sub-elements \"namespace\" can\'t appear after \"feature\".", NULL, 3); +} + +static struct lysp_submodule * +submod_renew(struct lysp_yin_ctx *ctx, const char *belongs_to) +{ + struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx; + struct lysp_submodule *submod; + + lys_module_free(&fctx, PARSER_CUR_PMOD(ctx)->mod, 0); + submod = calloc(1, sizeof *submod); + ctx->parsed_mods->objs[0] = submod; + submod->mod = calloc(1, sizeof *submod->mod); + lydict_insert(ly_ctx, belongs_to, 0, &submod->mod->name); + submod->mod->parsed = (struct lysp_module *)submod; + submod->mod->ctx = ly_ctx; + + fctx.mod = submod->mod; + + return submod; +} + +static void +test_submodule_elem(void **state) +{ + const char *data; + struct lysp_submodule *lysp_submod = submod_renew(YCTX, "module-name"); + + /* max subelements */ + data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">\n" + " <yang-version value=\"1.1\"/>\n" + " <belongs-to module=\"module-name\">\n" + " <prefix value=\"pref\"/>\n" + " </belongs-to>\n" + " <include module=\"b-mod\"/>\n" + " <import module=\"a-mod\"><prefix value=\"imp-pref\"/></import>\n" + " <organization><text>org</text></organization>\n" + " <contact><text>contact</text></contact>\n" + " <description><text>desc</text></description>\n" + " <reference><text>ref</text></reference>\n" + " <revision date=\"2019-02-02\"/>\n" + " <anydata name=\"anyd\"/>\n" + " <anyxml name=\"anyx\"/>\n" + " <choice name=\"choice\"/>\n" + " <container name=\"cont\"/>\n" + " <leaf name=\"leaf\"> <type name=\"type\"/> </leaf>\n" + " <leaf-list name=\"llist\"> <type name=\"type\"/> </leaf-list>\n" + " <list name=\"sub-list\"/>\n" + " <uses name=\"uses-name\"/>\n" + " <augment target-node=\"target\"/>\n" + " <deviation target-node=\"target\">\n" + " <deviate value=\"not-supported\"/>\n" + " </deviation>\n" + " <extension name=\"ext\"/>\n" + " <feature name=\"feature\"/>\n" + " <grouping name=\"grp\"/>\n" + " <identity name=\"ident-name\"/>\n" + " <notification name=\"notf\"/>\n" + " <rpc name=\"rpc-name\"/>\n" + " <typedef name=\"tpdf\"> <type name=\"type\"/> </typedef>\n" + EXT_SUBELEM "\n" + "</submodule>\n"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + + assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_SUCCESS); + CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (mod) are not necessary.", + NULL, 0); + assert_string_equal(lysp_submod->name, "mod"); + assert_string_equal(lysp_submod->revs[0].date, "2019-02-02"); + assert_string_equal(lysp_submod->prefix, "pref"); + assert_null(lysp_submod->filepath); + assert_string_equal(lysp_submod->org, "org"); + assert_string_equal(lysp_submod->contact, "contact"); + assert_string_equal(lysp_submod->dsc, "desc"); + assert_string_equal(lysp_submod->ref, "ref"); + assert_int_equal(lysp_submod->version, LYS_VERSION_1_1); + CHECK_LYSP_IMPORT(lysp_submod->imports, NULL, 0, "a-mod", + "imp-pref", NULL, ""); + assert_string_equal(lysp_submod->includes->name, "b-mod"); + assert_string_equal(lysp_submod->extensions->name, "ext"); + assert_string_equal(lysp_submod->features->name, "feature"); + assert_string_equal(lysp_submod->identities->name, "ident-name"); + assert_string_equal(lysp_submod->typedefs->name, "tpdf"); + assert_string_equal(lysp_submod->groupings->name, "grp"); + assert_string_equal(lysp_submod->data->name, "anyd"); + assert_int_equal(lysp_submod->data->nodetype, LYS_ANYDATA); + assert_string_equal(lysp_submod->data->next->name, "anyx"); + assert_int_equal(lysp_submod->data->next->nodetype, LYS_ANYXML); + assert_string_equal(lysp_submod->data->next->next->name, "choice"); + assert_int_equal(lysp_submod->data->next->next->nodetype, LYS_CHOICE); + assert_string_equal(lysp_submod->data->next->next->next->name, "cont"); + assert_int_equal(lysp_submod->data->next->next->next->nodetype, LYS_CONTAINER); + assert_string_equal(lysp_submod->data->next->next->next->next->name, "leaf"); + assert_int_equal(lysp_submod->data->next->next->next->next->nodetype, LYS_LEAF); + assert_string_equal(lysp_submod->data->next->next->next->next->next->name, "llist"); + assert_int_equal(lysp_submod->data->next->next->next->next->next->nodetype, LYS_LEAFLIST); + assert_string_equal(lysp_submod->data->next->next->next->next->next->next->name, "sub-list"); + assert_int_equal(lysp_submod->data->next->next->next->next->next->next->nodetype, LYS_LIST); + assert_string_equal(lysp_submod->data->next->next->next->next->next->next->next->name, "uses-name"); + assert_int_equal(lysp_submod->data->next->next->next->next->next->next->next->nodetype, LYS_USES); + assert_null(lysp_submod->data->next->next->next->next->next->next->next->next); + assert_string_equal(lysp_submod->augments->nodeid, "target"); + assert_string_equal(lysp_submod->rpcs->name, "rpc-name"); + assert_string_equal(lysp_submod->notifs->name, "notf"); + assert_string_equal(lysp_submod->deviations->nodeid, "target"); + TEST_1_CHECK_LYSP_EXT_INSTANCE(&(lysp_submod->exts[0]), LY_STMT_SUBMODULE); + + /* min subelemnts */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_submod = submod_renew(YCTX, "module-name"); + data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"submod\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"module-name\"><prefix value=\"pref\"/></belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_SUCCESS); + assert_string_equal(lysp_submod->prefix, "pref"); + assert_int_equal(lysp_submod->version, LYS_VERSION_1_0); + + /* incorrect subelem order */ + ly_in_free(UTEST_IN, 0); + lyxml_ctx_free(YCTX->xmlctx); + lysp_submod = submod_renew(YCTX, "module-name"); + data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"submod\">\n" + " <yang-version value=\"1\"/>\n" + " <reference><text>ref</text></reference>\n" + " <belongs-to module=\"module-name\"><prefix value=\"pref\"/></belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &UTEST_IN), LY_SUCCESS); + assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS); + assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_EVALID); + CHECK_LOG_CTX("Invalid order of submodule's sub-elements \"belongs-to\" can't appear after \"reference\".", NULL, 4); +} + +static void +test_yin_parse_module(void **state) +{ + const char *data; + struct lys_module *mod; + struct lysp_yin_ctx *yin_ctx = NULL; + struct ly_in *in = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"a\"> \n" + " <yang-version value=\"1.1\"/>\n" + " <namespace uri=\"urn:tests:extensions:metadata:a\"/>\n" + " <prefix value=\"a\"/>\n" + " <import module=\"ietf-yang-metadata\">\n" + " <prefix value=\"md\"/>\n" + " </import>\n" + " <feature name=\"f\"/>\n" + " <md:annotation name=\"x\">\n" + " <description>\n" + " <text>test</text>\n" + " </description>\n" + " <reference>\n" + " <text>test</text>\n" + " </reference>\n" + " <if-feature name=\"f\"/>\n" + " <status value=\"current\"/>\n" + " <type name=\"uint8\"/>\n" + " <units name=\"meters\"/>\n" + " </md:annotation>\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS); + assert_null(mod->parsed->exts->child->next->child); + assert_string_equal(mod->parsed->exts->child->next->arg, "test"); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module name=\"example-foo\"" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"" + " xmlns:foo=\"urn:example:foo\"" + " xmlns:myext=\"urn:example:extensions\">\n" + + " <yang-version value=\"1\"/>\n" + + " <namespace uri=\"urn:example:foo\"/>\n" + " <prefix value=\"foo\"/>\n" + + " <import module=\"example-extensions\">\n" + " <prefix value=\"myext\"/>\n" + " </import>\n" + + " <list name=\"interface\">\n" + " <key value=\"name\"/>\n" + " <leaf name=\"name\">\n" + " <type name=\"string\"/>\n" + " </leaf>\n" + " <leaf name=\"mtu\">\n" + " <type name=\"uint32\"/>\n" + " <description>\n" + " <text>The MTU of the interface.</text>\n" + " </description>\n" + " <myext:c-define name=\"MY_MTU\"/>\n" + " </leaf>\n" + " </list>\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <namespace uri=\"urn:example:foo\"/>\n" + " <prefix value=\"foo\"/>\n" + "</module>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<submodule name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">" + "</submodule>\n"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EINVAL); + CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + + mod = calloc(1, sizeof *mod); + mod->ctx = UTEST_LYCTX; + data = "<module name=\"example-foo\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <namespace uri=\"urn:example:foo\"/>\n" + " <prefix value=\"foo\"/>\n" + "</module>\n" + "<module>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EVALID); + CHECK_LOG_CTX("Trailing garbage \"<module>\" after module, expected end-of-input.", NULL, 6); + lys_module_free(&fctx, mod, 0); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + mod = NULL; + yin_ctx = NULL; +} + +static void +test_yin_parse_submodule(void **state) +{ + const char *data; + struct lysp_yin_ctx *yin_ctx = NULL; + struct lysp_submodule *submod = NULL; + struct ly_in *in; + + lydict_insert(UTEST_LYCTX, "a", 0, &PARSER_CUR_PMOD(YCTX)->mod->name); + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"asub\"" + " xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"" + " xmlns:a=\"urn:a\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + " <include module=\"atop\"/>\n" + " <feature name=\"fox\"/>\n" + " <notification name=\"bar-notif\">\n" + " <if-feature name=\"bar\"/>\n" + " </notification>\n" + " <notification name=\"fox-notif\">\n" + " <if-feature name=\"fox\"/>\n" + " </notification>\n" + " <augment target-node=\"/a_pref:top\">\n" + " <if-feature name=\"bar\"/>\n" + " <container name=\"bar-sub\"/>\n" + " </augment>\n" + " <augment target-node=\"/top\">\n" + " <container name=\"bar-sub2\"/>\n" + " </augment>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_SUCCESS); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_SUCCESS); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<module name=\"inval\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">" + "</module>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_EINVAL); + CHECK_LOG_CTX("Input data contains module when a submodule is expected.", NULL, 0); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; + + data = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + "</submodule>\n" + "<submodule name=\"asub\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n" + " <yang-version value=\"1\"/>\n" + " <belongs-to module=\"a\">\n" + " <prefix value=\"a_pref\"/>\n" + " </belongs-to>\n" + "</submodule>"; + assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS); + assert_int_equal(yin_parse_submodule(&yin_ctx, UTEST_LYCTX, (struct lysp_ctx *)YCTX, in, &submod), LY_EVALID); + CHECK_LOG_CTX("Trailing garbage \"<submodule name...\" after submodule, expected end-of-input.", NULL, 8); + lysp_module_free(&fctx, (struct lysp_module *)submod); + lysp_yin_ctx_free(yin_ctx); + ly_in_free(in, 0); + yin_ctx = NULL; + submod = NULL; +} + +int +main(void) +{ + + const struct CMUnitTest tests[] = { + UTEST(test_yin_match_keyword, setup, teardown), + UTEST(test_yin_parse_content, setup, teardown), + UTEST(test_validate_value, setup, teardown), + UTEST(test_valid_module), + UTEST(test_print_module), + UTEST(test_print_submodule), + + UTEST(test_yin_match_argument_name), + cmocka_unit_test_setup_teardown(test_enum_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_bit_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_status_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_yin_element_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_yangversion_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_argument_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_belongsto_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_config_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_default_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_err_app_tag_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_err_msg_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_fracdigits_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_iffeature_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_length_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_modifier_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_namespace_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_pattern_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_value_position_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_prefix_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_range_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_reqinstance_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_revision_date_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_unique_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_units_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_yin_text_value_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_type_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_max_elems_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_min_elems_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_ordby_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_any_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_leaf_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_leaf_list_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_presence_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_key_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_uses_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_list_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_notification_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_grouping_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_container_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_case_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_choice_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_inout_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_action_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_augment_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_deviate_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_deviation_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_module_elem, setup, teardown), + cmocka_unit_test_setup_teardown(test_submodule_elem, setup, teardown), + + cmocka_unit_test_setup_teardown(test_yin_parse_module, setup, teardown), + cmocka_unit_test_setup_teardown(test_yin_parse_submodule, setup, teardown), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} |