summaryrefslogtreecommitdiffstats
path: root/tests/utests/extensions/test_structure.c
blob: cc8ed71849bf546f4d65a555c0ebaa76704ab0d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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", 0);

    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", 0);

    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, 0);
    CHECK_LOG_CTX("Extension instance \"sx:structure\" missing argument element \"name\".", NULL, 0);

    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", 0);

    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", 0);

    /* 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'}", 0);
}

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);
}