diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:55:11 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:55:11 +0000 |
commit | cd07912073c951b4bbb871ed2653af1be2cfc714 (patch) | |
tree | 1073c2308492e6aea4c66cb7436ee92db2abfd42 /tests/utests/extensions | |
parent | Initial commit. (diff) | |
download | libyang2-upstream.tar.xz libyang2-upstream.zip |
Adding upstream version 2.1.30.upstream/2.1.30upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | tests/utests/extensions/test_metadata.c | 211 | ||||
-rw-r--r-- | tests/utests/extensions/test_nacm.c | 124 | ||||
-rw-r--r-- | tests/utests/extensions/test_schema_mount.c | 1566 | ||||
-rw-r--r-- | tests/utests/extensions/test_structure.c | 255 | ||||
-rw-r--r-- | tests/utests/extensions/test_yangdata.c | 273 |
5 files changed, 2429 insertions, 0 deletions
diff --git a/tests/utests/extensions/test_metadata.c b/tests/utests/extensions/test_metadata.c new file mode 100644 index 0000000..39d29be --- /dev/null +++ b/tests/utests/extensions/test_metadata.c @@ -0,0 +1,211 @@ +/** + * @file test_metadata.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for Metadata extension (annotation) support + * + * Copyright (c) 2019 - 2022 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "libyang.h" +#include "plugins_exts.h" +#include "plugins_exts/metadata.h" + +static void +test_yang(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + const char *units; + + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:metadata:a; prefix a;" + "import ietf-yang-metadata {prefix md;}" + "feature f;" + "md:annotation x {" + " description \"test\";" + " if-feature f;" + " reference \"test\";" + " status \"current\";" + " type uint8;" + " units meters;" + "}}"; + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, feats, &mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->compiled->exts)); + e = &mod->compiled->exts[0]; + assert_non_null(e->compiled); + assert_non_null(e->substmts); + lyplg_ext_get_storage(e, LY_STMT_UNITS, sizeof units, (const void **)&units); + assert_string_equal("meters", units); + + /* invalid */ + /* missing mandatory type substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".", + "/aa:{extension='md:annotation'}/aa"); + + /* not allowed substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {default x;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.", + "/aa:{extension='md:annotation'}/aa"); + + /* invalid cardinality of units substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string; units x; units y;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"units\".", + "/aa:{extension='md:annotation'}/aa"); + + /* invalid cardinality of status substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string; status current; status obsolete;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"status\".", + "/aa:{extension='md:annotation'}/aa"); + + /* invalid cardinality of status substatement */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string; type uint8;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"type\".", + "/aa:{extension='md:annotation'}/aa"); + + /* duplication of the same annotation */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;" + "import ietf-yang-metadata {prefix md;}" + "md:annotation aa {type string;} md:annotation aa {type uint8;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.", + "/aa:{extension='md:annotation'}/aa"); +} + +static void +test_yin(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + const char *data, *units; + + 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\"/><namespace uri=\"urn:tests:extensions:metadata:a\"/><prefix value=\"a\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<feature name=\"f\"/>\n" + "<md:annotation name=\"x\">\n" + " <description><text>test</text></description>\n" + " <reference><text>test</text></reference>\n" + " <if-feature name=\"f\"/>\n" + " <status value=\"current\"/>\n" + " <type name=\"uint8\"/>\n" + " <units name=\"meters\"/>\n" + "</md:annotation></module>"; + const char *feats[] = {"f", NULL}; + + UTEST_ADD_MODULE(data, LYS_IN_YIN, feats, &mod); + assert_int_equal(1, LY_ARRAY_COUNT(mod->compiled->exts)); + e = &mod->compiled->exts[0]; + assert_non_null(e->compiled); + assert_non_null(e->substmts); + lyplg_ext_get_storage(e, LY_STMT_UNITS, sizeof units, (const void **)&units); + assert_string_equal("meters", units); + + /* invalid */ + /* missing mandatory type substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\"/>\n" + "</module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".", + "/aa:{extension='md:annotation'}/aa"); + + /* not allowed substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <default value=\"x\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.", + "/aa:{extension='md:annotation'}/aa"); + + /* invalid cardinality of units substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + " <units name=\"x\"/>\n" + " <units name=\"y\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"units\".", + "/aa:{extension='md:annotation'}/aa"); + + /* invalid cardinality of status substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + " <status value=\"current\"/>\n" + " <status value=\"obsolete\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"status\".", + "/aa:{extension='md:annotation'}/aa"); + + /* invalid cardinality of status substatement */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + " <type name=\"uint8\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Duplicate keyword \"type\".", + "/aa:{extension='md:annotation'}/aa"); + + /* duplication of the same annotation */ + data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n" + "<yang-version value=\"1.1\"/><namespace uri=\"urn:tests:extensions:metadata:aa\"/><prefix value=\"aa\"/>\n" + "<import module=\"ietf-yang-metadata\"><prefix value=\"md\"/></import>\n" + "<md:annotation name=\"aa\">\n" + " <type name=\"string\"/>\n" + "</md:annotation><md:annotation name=\"aa\">\n" + " <type name=\"uint8\"/>\n" + "</md:annotation></module>"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.", + "/aa:{extension='md:annotation'}/aa"); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_yang), + UTEST(test_yin), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_nacm.c b/tests/utests/extensions/test_nacm.c new file mode 100644 index 0000000..1c999fb --- /dev/null +++ b/tests/utests/extensions/test_nacm.c @@ -0,0 +1,124 @@ +/* + * @file test_nacm.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for NACM extensions support + * + * Copyright (c) 2019-2020 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ +#define _UTEST_MAIN_ +#include "utests.h" + +#include "libyang.h" + +static int +setup(void **state) +{ + UTEST_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-netconf-acm", "2018-02-14", NULL)); + + return 0; +} + +static void +test_deny_all(void **state) +{ + struct lys_module *mod; + struct lysc_node_container *cont; + struct lysc_node_leaf *leaf; + struct lysc_ext_instance *e; + + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "container a { nacm:default-deny-all; leaf aa {type string;}}" + "leaf b {type string;}}"; + + /* valid data */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(cont = (struct lysc_node_container *)mod->compiled->data); + assert_non_null(leaf = (struct lysc_node_leaf *)cont->child); + assert_non_null(e = &cont->exts[0]); + assert_int_equal(LY_ARRAY_COUNT(cont->exts), 1); + assert_int_equal(LY_ARRAY_COUNT(leaf->exts), 1); /* NACM extensions inherit */ + assert_ptr_equal(e->def, leaf->exts[0].def); + assert_int_equal(1, *((uint8_t *)e->compiled)); /* plugin's value for default-deny-all */ + assert_null(cont->next->exts); + + /* ignored - valid with warning */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:nacm:b; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "nacm:default-deny-all;}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-all is allowed only in a data nodes, but it is placed in \"module\" statement.", + "/b:{extension='nacm:default-deny-all'}"); + + /* invalid */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "leaf l { type string; nacm:default-deny-all; nacm:default-deny-write;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.", + "/aa:l/{extension='nacm:default-deny-all'}"); +} + +static void +test_deny_write(void **state) +{ + struct lys_module *mod; + struct lysc_node_container *cont; + struct lysc_node_leaf *leaf; + struct lysc_ext_instance *e; + + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "container a { nacm:default-deny-write; leaf aa {type string;}}" + "leaf b {type string;}}"; + + /* valid data */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(cont = (struct lysc_node_container *)mod->compiled->data); + assert_non_null(leaf = (struct lysc_node_leaf *)cont->child); + assert_non_null(e = &cont->exts[0]); + assert_int_equal(LY_ARRAY_COUNT(cont->exts), 1); + assert_int_equal(LY_ARRAY_COUNT(leaf->exts), 1); /* NACM extensions inherit */ + assert_ptr_equal(e->def, leaf->exts[0].def); + assert_int_equal(2, *((uint8_t *)e->compiled)); /* plugin's value for default-deny-write */ + + /* ignored - valid with warning */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:nacm:b; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "notification notif {nacm:default-deny-write;}}"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-write is not allowed in notification statement.", + "/b:notif/{extension='nacm:default-deny-write'}"); + + /* invalid */ + data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;" + "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}" + "leaf l { type string; nacm:default-deny-write; nacm:default-deny-write;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": " + "Extension nacm:default-deny-write is instantiated multiple times.", + "/aa:l/{extension='nacm:default-deny-write'}"); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_deny_all, setup), + UTEST(test_deny_write, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_schema_mount.c b/tests/utests/extensions/test_schema_mount.c new file mode 100644 index 0000000..be879ec --- /dev/null +++ b/tests/utests/extensions/test_schema_mount.c @@ -0,0 +1,1566 @@ +/** + * @file test_schema_mount.c + * @author Tadeas Vintrlik <xvintr04@stud.fit.vutbr.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for Schema Mount extension support + * + * Copyright (c) 2021 - 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 "libyang.h" + +void **glob_state; + +static int +setup(void **state) +{ + const char *schema = + "module sm {yang-version 1.1;namespace \"urn:sm\";prefix \"sm\";" + "import ietf-yang-schema-mount {prefix yangmnt;}" + "import ietf-interfaces {prefix if;}" + "container root {yangmnt:mount-point \"root\";}" + "container root2 {yangmnt:mount-point \"root\";}" + "container root3 {" + " list ls { key name; leaf name {type string;}" + " yangmnt:mount-point \"mnt-root\";" + " }" + "}" + "container root4 {config false; yangmnt:mount-point \"root\";}" + "leaf target{type string;}" + "augment /if:interfaces/if:interface {" + " leaf sm-name {type leafref {path \"/sm:target\";}}" + "}" + "}"; + + UTEST_SETUP; + glob_state = state; + + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL)); + + return 0; +} + +static void +test_schema(void **state) +{ + struct lys_module *mod; + const char *schema; + char *str; + + /* invalid */ + schema = + "module sm {\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " container root {\n" + " yangmnt:mount-point \"root\";\n" + " }\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Extension \"yangmnt:mount-point\" instance not allowed in YANG version 1 module.", + "/sm:root/{extension='yangmnt:mount-point'}/root"); + + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " yangmnt:mount-point \"root\";\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.", + "/sm:{extension='yangmnt:mount-point'}/root"); + + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " container root {\n" + " leaf l {\n" + " type empty;\n" + " yangmnt:mount-point \"root\";\n" + " }\n" + " }\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.", + "/sm:root/l/{extension='yangmnt:mount-point'}/root"); + + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " list l {\n" + " key \"k\";\n" + " leaf k {\n" + " type string;\n" + " }\n" + " yangmnt:mount-point \"root\";\n" + " yangmnt:mount-point \"root2\";\n" + " }\n" + "}\n"; + assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Multiple extension \"yangmnt:mount-point\" instances.", + "/sm:l/{extension='yangmnt:mount-point'}/root"); + + /* valid */ + schema = + "module sm {\n" + " yang-version 1.1;\n" + " namespace \"urn:sm\";\n" + " prefix sm;\n" + "\n" + " import ietf-yang-schema-mount {\n" + " prefix yangmnt;\n" + " }\n" + "\n" + " container root {\n" + " yangmnt:mount-point \"root\";\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, &mod)); + lys_print_mem(&str, mod, LYS_OUT_YANG, 0); + assert_string_equal(str, schema); + free(str); +} + +static LY_ERR +test_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free) +{ + void **state = glob_state; + struct lyd_node *data = NULL; + + (void)ext; + + if (user_data) { + CHECK_PARSE_LYD_PARAM(user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + } + + *ext_data = data; + *ext_data_free = 1; + return LY_SUCCESS; +} + +static void +test_parse_invalid(void **state) +{ + const char *xml, *json; + struct lyd_node *data; + + /* no callback set */ + xml = + "<root xmlns=\"urn:sm\">" + " <unknown xmlns=\"unknown\">" + " <interface>" + " <name>bu</name>" + " <type xmlns:ii=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ii:ethernetCsmacd</type>" + " </interface>" + " </unknown>" + "</root>"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EINVAL, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Failed to get extension data, no callback set.", + NULL); + + json = + "{" + " \"sm:root\": {" + " \"unknown:unknown\": {" + " \"interface\": [" + " {" + " \"name\": \"bu\"," + " \"type\": \"iana-if-type:ethernetCsmacd\"" + " }" + " ]" + " }" + " }" + "}"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EINVAL, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": Failed to get extension data, no callback set.", + NULL); + + /* unknown data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, NULL); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + assert_string_equal(LYD_NAME(data), "root"); + assert_null(lyd_child(data)); + assert_non_null(data->next); + assert_true(data->next->flags & LYD_DEFAULT); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("No module with namespace \"unknown\" in the context.", + "Data location \"/sm:root\", line number 1."); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + assert_string_equal(LYD_NAME(data), "root"); + assert_null(lyd_child(data)); + assert_non_null(data->next); + assert_true(data->next->flags & LYD_DEFAULT); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("No module named \"unknown\" in the context.", + "Data location \"/sm:root\", line number 1."); + + /* missing required callback data */ + xml = + "<root xmlns=\"urn:sm\">" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">" + " <interface>" + " <name>bu</name>" + " </interface>" + " </interfaces>" + "</root>"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", + "Data location \"/sm:root\", line number 1."); + + json = + "{" + " \"sm:root\": {" + " \"ietf-interfaces:interfaces\": {" + " \"interface\": [" + " {" + " \"name\": \"bu\"" + " }" + " ]" + " }" + " }" + "}"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", + "Data location \"/sm:root\", line number 1."); + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", + "Data location \"/sm:root\", line number 1."); + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", + "Data location \"/sm:root\", line number 1."); + + /* missing module in yang-library data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", + "Data location \"/sm:root\", line number 1."); + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Node \"interfaces\" not found as a child of \"root\" node.", + "Data location \"/sm:root\", line number 1."); + + /* callback data correct, invalid YANG data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Mandatory node \"type\" instance does not exist.", + "Schema location \"/ietf-interfaces:interfaces/interface/type\"."); + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Mandatory node \"type\" instance does not exist.", + "Schema location \"/ietf-interfaces:interfaces/interface/type\"."); + + /* same validation fail in separate validation */ + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data); + assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Mandatory node \"type\" instance does not exist.", + "Schema location \"/ietf-interfaces:interfaces/interface/type\"."); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data); + assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Mandatory node \"type\" instance does not exist.", + "Schema location \"/ietf-interfaces:interfaces/interface/type\"."); + lyd_free_siblings(data); + + /* success */ + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); +} + +static void +test_parse_inline(void **state) +{ + const char *xml, *json; + char *lyb; + struct lyd_node *data; + const struct ly_ctx *ext_ctx; + + /* valid */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + ext_ctx = LYD_CTX(lyd_child(data)); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"oper-status\": \"not-present\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data))); + lyd_free_siblings(data); + + /* different yang-lib data with the same content-id */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>ietf-ip</name>" + " <revision>2014-06-16</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_ptr_not_equal(ext_ctx, LYD_CTX(lyd_child(data))); + ext_ctx = LYD_CTX(lyd_child(data)); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data))); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, 0)); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + assert_ptr_equal(ext_ctx, LYD_CTX(lyd_child(data))); + free(lyb); + lyd_free_siblings(data); +} + +static void +test_parse_shared(void **state) +{ + const char *xml, *json; + char *lyb; + struct lyd_node *data; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"oper-status\": \"not-present\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + /* different yang-lib data */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>ietf-ip</name>" + " <revision>2014-06-16</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>2</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root2 xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root2>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Shared-schema yang-library content-id \"2\" differs from \"1\" used previously.", + "/ietf-yang-library:yang-library/content-id"); + + /* data for 2 mount points */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n" + "<root2 xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>fu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:fddi</type>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>fu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:fddi</type>\n" + " <oper-status>down</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2020-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root2>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"oper-status\": \"not-present\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2022-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " },\n" + " \"sm:root2\": {\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"fu\",\n" + " \"type\": \"iana-if-type:fddi\"\n" + " }\n" + " ]\n" + " },\n" + " \"ietf-interfaces:interfaces-state\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"fu\",\n" + " \"type\": \"iana-if-type:fddi\",\n" + " \"oper-status\": \"down\",\n" + " \"statistics\": {\n" + " \"discontinuity-time\": \"2020-01-01T10:00:00-00:00\"\n" + " }\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + + assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, LYD_PRINT_WITHSIBLINGS)); + lyd_free_siblings(data); + + CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + free(lyb); + lyd_free_siblings(data); +} + +static void +test_parse_shared_parent_ref(void **state) +{ + const char *xml, *json; + struct lyd_node *data; + + /* wrong leafref value */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>sm</name>" + " <namespace>urn:sm</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <namespace>" + " <prefix>smp</prefix>" + " <uri>urn:sm</uri>" + " </namespace>" + " <mount-point>" + " <module>sm</module>" + " <label>mnt-root</label>" + " <shared-schema>" + " <parent-reference>/smp:target[. = current()/smp:name]</parent-reference>" + " </shared-schema>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root3 xmlns=\"urn:sm\">\n" + " <ls>\n" + " <name>target-value</name>\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <sm-name xmlns=\"urn:sm\">target-value</sm-name>\n" + " </interface>\n" + " </interfaces>\n" + " </ls>\n" + "</root3>\n" + "<target xmlns=\"urn:sm\">wrong-target-value</target>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Invalid leafref value \"target-value\" - no target instance \"/sm:target\" with the same value.", + "Data location \"/ietf-interfaces:interfaces/interface[name='bu']/sm:sm-name\"."); + + json = + "{\n" + " \"sm:root3\": {\n" + " \"ls\": [" + " {\n" + " \"name\": \"target-value\",\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"sm:sm-name\": \"target-value\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"sm:target\": \"wrong-target-value\"\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data); + CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": " + "Invalid leafref value \"target-value\" - no target instance \"/sm:target\" with the same value.", + "Data location \"/ietf-interfaces:interfaces/interface[name='bu']/sm:sm-name\"."); + + /* success */ + xml = + "<root3 xmlns=\"urn:sm\">\n" + " <ls>\n" + " <name>target-value</name>\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <sm-name xmlns=\"urn:sm\">target-value</sm-name>\n" + " </interface>\n" + " </interfaces>\n" + " </ls>\n" + "</root3>\n" + "<target xmlns=\"urn:sm\">target-value</target>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + json = + "{\n" + " \"sm:root3\": {\n" + " \"ls\": [\n" + " {\n" + " \"name\": \"target-value\",\n" + " \"ietf-interfaces:interfaces\": {\n" + " \"interface\": [\n" + " {\n" + " \"name\": \"bu\",\n" + " \"type\": \"iana-if-type:ethernetCsmacd\",\n" + " \"sm:sm-name\": \"target-value\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + " ]\n" + " },\n" + " \"sm:target\": \"target-value\"\n" + "}\n"; + CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); +} + +static void +test_parse_config(void **state) +{ + const char *xml; + char *lyb; + struct lyd_node *data; + const struct lyd_node *node; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <config>false</config>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " </interface>\n" + " </interfaces>\n" + "</root>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + + node = lyd_child(data); + assert_string_equal(LYD_NAME(node), "interfaces"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "interface"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "name"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = node->next; + assert_string_equal(LYD_NAME(node), "type"); + assert_true(node->schema->flags & LYS_CONFIG_R); + + lyd_print_mem(&lyb, data, LYD_LYB, 0); + lyd_free_siblings(data); + CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + free(lyb); + + node = lyd_child(data); + assert_string_equal(LYD_NAME(node), "interfaces"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "interface"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "name"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = node->next; + assert_string_equal(LYD_NAME(node), "type"); + assert_true(node->schema->flags & LYS_CONFIG_R); + + lyd_free_siblings(data); + + /* the same effect but use a config false mount point instead of the separate metadata node */ + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <inline/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root4 xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <enabled>true</enabled>\n" + " </interface>\n" + " </interfaces>\n" + "</root4>\n"; + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data); + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + + node = lyd_child(data->next->next->next); + assert_string_equal(LYD_NAME(node), "interfaces"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "interface"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = lyd_child(node); + assert_string_equal(LYD_NAME(node), "name"); + assert_true(node->schema->flags & LYS_CONFIG_R); + node = node->next; + assert_string_equal(LYD_NAME(node), "type"); + assert_true(node->schema->flags & LYS_CONFIG_R); + + lyd_free_siblings(data); +} + +static void +test_new(void **state) +{ + const char *xml; + const struct lys_module *mod; + struct lyd_node *data, *node; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, + "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" " + " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">" + " <module-set>" + " <name>test-set</name>" + " <module>" + " <name>ietf-datastores</name>" + " <revision>2018-02-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-library</name>" + " <revision>2019-01-04</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>" + " </module>" + " <module>" + " <name>ietf-yang-schema-mount</name>" + " <revision>2019-01-14</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>" + " </module>" + " <module>" + " <name>ietf-interfaces</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>" + " </module>" + " <module>" + " <name>iana-if-type</name>" + " <revision>2014-05-08</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>" + " </module>" + " <module>" + " <name>ietf-ip</name>" + " <revision>2014-06-16</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-ip</namespace>" + " </module>" + " <import-only-module>" + " <name>ietf-yang-types</name>" + " <revision>2013-07-15</revision>" + " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>" + " </import-only-module>" + " </module-set>" + " <schema>" + " <name>test-schema</name>" + " <module-set>test-set</module-set>" + " </schema>" + " <datastore>" + " <name>ds:running</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <datastore>" + " <name>ds:operational</name>" + " <schema>test-schema</schema>" + " </datastore>" + " <content-id>1</content-id>" + "</yang-library>" + "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">" + " <module-set-id>1</module-set-id>" + "</modules-state>" + "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">" + " <mount-point>" + " <module>sm</module>" + " <label>root</label>" + " <shared-schema/>" + " </mount-point>" + "</schema-mounts>"); + xml = + "<root xmlns=\"urn:sm\">\n" + " <interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <ipv4 xmlns=\"urn:ietf:params:xml:ns:yang:ietf-ip\">\n" + " <enabled>false</enabled>\n" + " </ipv4>\n" + " </interface>\n" + " </interfaces>\n" + " <interfaces-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">\n" + " <interface>\n" + " <name>bu</name>\n" + " <type xmlns:ianaift=\"urn:ietf:params:xml:ns:yang:iana-if-type\">ianaift:ethernetCsmacd</type>\n" + " <oper-status>not-present</oper-status>\n" + " <statistics>\n" + " <discontinuity-time>2022-01-01T10:00:00-00:00</discontinuity-time>\n" + " </statistics>\n" + " </interface>\n" + " </interfaces-state>\n" + "</root>\n"; + + /* create the data manually with simple new functions */ + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "sm"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "root", 0, &data)); + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-interfaces"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(data, mod, "interfaces", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_list(node, NULL, "interface", 0, &node, "bu")); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "type", "iana-if-type:ethernetCsmacd", 0, NULL)); + mod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-ip"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(node, mod, "ipv4", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "enabled", "false", 0, NULL)); + + mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "ietf-interfaces"); + assert_non_null(mod); + assert_int_equal(LY_SUCCESS, lyd_new_inner(data, mod, "interfaces-state", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_list(node, NULL, "interface", 0, &node, "bu")); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "type", "iana-if-type:ethernetCsmacd", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "oper-status", "not-present", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_inner(node, NULL, "statistics", 0, &node)); + assert_int_equal(LY_SUCCESS, lyd_new_term(node, NULL, "discontinuity-time", "2022-01-01T10:00:00-00:00", 0, NULL)); + + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); + + /* create the data using lyd_new_path */ + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, + "/sm:root/ietf-interfaces:interfaces/interface[name='bu']/type", "iana-if-type:ethernetCsmacd", 0, &data)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces/interface[name='bu']/ietf-ip:ipv4/enabled", "false", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/type", "iana-if-type:ethernetCsmacd", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/oper-status", "not-present", 0, NULL)); + assert_int_equal(LY_SUCCESS, lyd_new_path(data, NULL, + "/sm:root/ietf-interfaces:interfaces-state/interface[name='bu']/statistics/discontinuity-time", + "2022-01-01T10:00:00-00:00", 0, NULL)); + + CHECK_LYD_STRING_PARAM(data, xml, LYD_XML, LYD_PRINT_WITHSIBLINGS); + lyd_free_siblings(data); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema), + UTEST(test_parse_invalid, setup), + UTEST(test_parse_inline, setup), + UTEST(test_parse_shared, setup), + UTEST(test_parse_shared_parent_ref, setup), + UTEST(test_parse_config, setup), + UTEST(test_new, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_structure.c b/tests/utests/extensions/test_structure.c new file mode 100644 index 0000000..23af450 --- /dev/null +++ b/tests/utests/extensions/test_structure.c @@ -0,0 +1,255 @@ +/** + * @file test_structure.c + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief unit tests for structure extensions support + * + * Copyright (c) 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 "libyang.h" + +static void +test_schema(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + char *printed = NULL; + const char *data, *info; + + /* valid data */ + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct {" + " must \"/n2/l\";" + " status deprecated;" + " description desc;" + " reference no-ref;" + " typedef my-type {type string;}" + " grouping my-grp {leaf gl {type my-type;}}" + " container n1 {leaf l {config false; type uint32;}}" + " list n2 {leaf l {type leafref {path /n1/l;}}}" + " uses my-grp;" + "}}"; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + + /* valid augment data */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;" + "import ietf-yang-structure-ext {prefix sx;}" + "import a {prefix a;}" + "sx:augment-structure \"/a:struct/a:n1\" {" + " status obsolete;" + " reference none;" + " leaf aug-leaf {type string;}" + "}}"; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + + /* yang compiled print */ + info = "module a {\n" + " namespace \"urn:tests:extensions:structure:a\";\n" + " prefix a;\n" + "\n" + " ietf-yang-structure-ext:structure \"struct\" {\n" + " must \"/n2/l\";\n" + " status deprecated;\n" + " description\n" + " \"desc\";\n" + " reference\n" + " \"no-ref\";\n" + " container n1 {\n" + " status deprecated;\n" + " leaf l {\n" + " type uint32;\n" + " status deprecated;\n" + " }\n" + " leaf aug-leaf {\n" + " type string;\n" + " status obsolete;\n" + " }\n" + " }\n" + " list n2 {\n" + " min-elements 0;\n" + " max-elements 4294967295;\n" + " ordered-by user;\n" + " status deprecated;\n" + " leaf l {\n" + " type leafref {\n" + " path \"/n1/l\";\n" + " require-instance true;\n" + " type uint32;\n" + " }\n" + " status deprecated;\n" + " }\n" + " }\n" + " leaf gl {\n" + " type string;\n" + " status deprecated;\n" + " }\n" + " }\n" + "}\n"; + + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "a")); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + info = "module b {\n" + " namespace \"urn:tests:extensions:structure:b\";\n" + " prefix b;\n" + "\n" + " ietf-yang-structure-ext:augment-structure \"/a:struct/a:n1\";\n" + "}\n"; + + assert_non_null(mod = ly_ctx_get_module_implemented(UTEST_LYCTX, "b")); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + /* no substatements */ + data = "module c {yang-version 1.1; namespace urn:tests:extensions:structure:c; prefix c;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct;}"; + info = "module c {\n" + " namespace \"urn:tests:extensions:structure:c\";\n" + " prefix c;\n" + "\n" + " ietf-yang-structure-ext:structure \"struct\";\n" + "}\n"; + + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); +} + +static void +test_schema_invalid(void **state) +{ + const char *data; + + /* structure */ + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct {import yang;}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Invalid keyword \"import\" as a child of \"sx:structure struct\" extension instance.", + "/a:{extension='sx:structure'}/struct"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "container b { sx:structure struct { container x { leaf x {type string;}}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": " + "Extension sx:structure must not be used as a non top-level statement in \"container\" statement.", + "/a:b/{extension='sx:structure'}/struct"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure { container x { leaf x {type string;}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, + "Extension instance \"sx:structure\" missing argument element \"name\".", NULL); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct { container x { leaf x {type string;}}}" + "sx:structure struct { container y { leaf y {type string;}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure is instantiated multiple times.", + "/a:{extension='sx:structure'}/struct"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct { container x { leaf x {type string;}}}" + "choice struct { container y { leaf y {type string;}}}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID); + CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure collides with a choice with the same identifier.", + "/a:{extension='sx:structure'}/struct"); + + /* augment-structure */ + data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct {" + " container n1 {leaf l {config false; type uint32;}}" + " list n2 {leaf l {type string;}}" + "}" + "container n1 {leaf l2 {type uint8;}}}"; + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, NULL); + + data = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;" + "import ietf-yang-structure-ext {prefix sx;}" + "import a {prefix a;}" + "sx:augment-structure \"/a:n1\" {" + " leaf aug-leaf {type string;}" + "}}"; + UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_ENOTFOUND); + CHECK_LOG_CTX("Augment extension target node \"/a:n1\" from module \"b\" was not found.", + "/b:{extension='sx:augment-structure'}/{augment='/a:n1'}"); +} + +static void +test_parse(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + struct lyd_node *tree = NULL; + const char *yang; + const char *xml = "<x xmlns=\"urn:tests:extensions:structure:a\">" + "<x>test</x>" + "<x2 xmlns=\"urn:tests:extensions:structure:b\">25</x2>" + "</x>"; + const char *json = "{\"a:x\":{\"x\":\"test\",\"b:x2\":25}}"; + + yang = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure struct { container x { leaf x { type string;}}}}"; + UTEST_ADD_MODULE(yang, LYS_IN_YANG, NULL, &mod); + + yang = "module b {yang-version 1.1; namespace urn:tests:extensions:structure:b; prefix b;" + "import ietf-yang-structure-ext {prefix sx;}" + "import a {prefix a;}" + "sx:augment-structure \"/a:struct/a:x\" {" + " leaf x2 {type uint32;}" + "}}"; + UTEST_ADD_MODULE(yang, LYS_IN_YANG, NULL, NULL); + + /* get extension after recompilation */ + assert_non_null(e = mod->compiled->exts); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(xml, &UTEST_IN)); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); + + ly_in_memory(UTEST_IN, json); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema), + UTEST(test_schema_invalid), + UTEST(test_parse), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/utests/extensions/test_yangdata.c b/tests/utests/extensions/test_yangdata.c new file mode 100644 index 0000000..8c0176f --- /dev/null +++ b/tests/utests/extensions/test_yangdata.c @@ -0,0 +1,273 @@ +/* + * @file test_yangdata.c + * @author: Radek Krejci <rkrejci@cesnet.cz> + * @brief unit tests for yang-data extensions support + * + * 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 "libyang.h" + +static int +setup(void **state) +{ + UTEST_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)); + + return 0; +} + +static void +test_schema(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + char *printed = NULL; + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "feature x;" + "rc:yang-data template { container x { list l { leaf x { type string;}} leaf y {if-feature x; type string; config false;}}}}"; + const char *info = "module a {\n" + " namespace \"urn:tests:extensions:yangdata:a\";\n" + " prefix self;\n\n" + " ietf-restconf:yang-data \"template\" {\n" + " container x {\n" + " status current;\n" + " list l {\n" /* no key */ + " min-elements 0;\n" + " max-elements 4294967295;\n" + " ordered-by user;\n" + " status current;\n" + " leaf x {\n" + " type string;\n" + " status current;\n" + " }\n" + " }\n" + " leaf y {\n" /* config and if-feature are ignored */ + " type string;\n" + " status current;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + + /* valid data */ + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + data = "module c {yang-version 1.1; namespace urn:tests:extensions:yangdata:c; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "grouping g { choice ch { container a {presence a; config false;} container b {presence b; config true;}}}" + "rc:yang-data template { uses g;}}"; + info = "module c {\n" + " namespace \"urn:tests:extensions:yangdata:c\";\n" + " prefix self;\n\n" + " ietf-restconf:yang-data \"template\" {\n" + " choice ch {\n" + " status current;\n" + " case a {\n" + " status current;\n" + " container a {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n" + " case b {\n" + " status current;\n" + " container b {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 1); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + /* ignored - valid with warning */ + data = "module b {yang-version 1.1; namespace urn:tests:extensions:yangdata:b; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "container b { rc:yang-data template { container x { leaf x {type string;}}}}}"; + info = "module b {\n" + " namespace \"urn:tests:extensions:yangdata:b\";\n" + " prefix self;\n\n" + " container b {\n" + " config true;\n" + " status current;\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_null(mod->compiled->exts); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is ignored since it appears as a non top-level statement in \"container\" statement.", + "/b:b/{extension='rc:yang-data'}/template"); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); + + /* sama data nodes name, but not conflicting */ + data = "module d {yang-version 1.1; namespace urn:tests:extensions:yangdata:d; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "leaf d { type string;}" + "rc:yang-data template1 { container d {presence d;}}" + "rc:yang-data template2 { container d {presence d;}}}"; + info = "module d {\n" + " namespace \"urn:tests:extensions:yangdata:d\";\n" + " prefix self;\n\n" + " ietf-restconf:yang-data \"template1\" {\n" + " container d {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n" + " ietf-restconf:yang-data \"template2\" {\n" + " container d {\n" + " presence \"true\";\n" + " status current;\n" + " }\n" + " }\n\n" + " leaf d {\n" + " type string;\n" + " config true;\n" + " status current;\n" + " }\n" + "}\n"; + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + assert_int_equal(LY_ARRAY_COUNT(mod->compiled->exts), 2); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0)); + assert_string_equal(printed, info); + free(printed); +} + +static void +test_schema_invalid(void **state) +{ + const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { leaf x {type string;}}}"; + + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Invalid keyword \"leaf\" as a child of \"rc:yang-data template\" extension instance.", + "/a:{extension='rc:yang-data'}/template"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { choice x { leaf x {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with leaf top level data node (inside a choice), " + "but only a single container data node is allowed.", + "/a:{extension='rc:yang-data'}/template"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { choice x { case x { container z {presence ppp;} leaf x {type string;}}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with multiple top level data nodes (inside a single choice's case), " + "but only a single container data node is allowed.", + "/a:{extension='rc:yang-data'}/template"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { container x { leaf x {type string;}} container y { leaf y {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with multiple top level data nodes, " + "but only a single container data node is allowed.", + "/a:{extension='rc:yang-data'}/template"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template;}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated without any top level data node, " + "but exactly one container data node is expected.", + "/a:{extension='rc:yang-data'}/template"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data { container x { leaf x {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL, + "Extension instance \"rc:yang-data\" missing argument element \"name\".", NULL); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { container x { leaf x {type string;}}}" + "rc:yang-data template { container y { leaf y {type string;}}}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated multiple times.", + "/a:{extension='rc:yang-data'}/template"); + + data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "grouping t { leaf-list x {type string;}}" + "rc:yang-data template { uses t;}}"; + assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL)); + CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": " + "Extension rc:yang-data is instantiated with leaf-list top level data node, " + "but only a single container data node is allowed.", + "/a:{extension='rc:yang-data'}/template"); +} + +static void +test_parse(void **state) +{ + struct lys_module *mod; + struct lysc_ext_instance *e; + struct lyd_node *tree = NULL; + const char *schema = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;" + "import ietf-restconf {revision-date 2017-01-26; prefix rc;}" + "rc:yang-data template { container x { leaf x { type string;}}}}"; + const char *xml = "<x xmlns=\"urn:tests:extensions:yangdata:a\"><x>test</x></x>"; + const char *json = "{\"a:x\":{\"x\":\"test\"}}"; + + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, &mod)); + assert_non_null(e = mod->compiled->exts); + + assert_int_equal(LY_SUCCESS, ly_in_new_memory(xml, &UTEST_IN)); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, xml, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + lyd_free_all(tree); + + ly_in_memory(UTEST_IN, json); + assert_int_equal(LY_SUCCESS, lyd_parse_ext_data(e, NULL, UTEST_IN, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree)); + CHECK_LYD_STRING_PARAM(tree, json, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS); + + lyd_free_all(tree); +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + UTEST(test_schema, setup), + UTEST(test_schema_invalid, setup), + UTEST(test_parse, setup), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} |