blob: 9301f7dc27b085f55a72f1829d225320c148ce0e [file] [log] [blame]
Michal Vasko14795a42020-05-22 16:44:44 +02001/*
2 * @file test_xpath.c
3 * @author: Michal Vasko <mvasko@cesnet.cz>
4 * @brief unit tests for XPath evaluation
5 *
6 * Copyright (c) 2020 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Michal Vasko14795a42020-05-22 16:44:44 +020015#include <stdarg.h>
16#include <stddef.h>
17#include <setjmp.h>
18#include <cmocka.h>
19
20#include <stdio.h>
21#include <string.h>
22
Radek Krejci70593c12020-06-13 20:48:09 +020023#include "context.h"
Radek Krejci7931b192020-06-25 17:05:03 +020024#include "parser_data.h"
Radek Krejci70593c12020-06-13 20:48:09 +020025#include "set.h"
26#include "tests/config.h"
27#include "tree_data.h"
28#include "tree_schema.h"
Michal Vasko14795a42020-05-22 16:44:44 +020029
30#define BUFSIZE 1024
31char logbuf[BUFSIZE] = {0};
32int store = -1; /* negative for infinite logging, positive for limited logging */
33
34struct ly_ctx *ctx; /* context for tests */
35
36/* set to 0 to printing error messages to stderr instead of checking them in code */
37#define ENABLE_LOGGER_CHECKING 1
38
39#if ENABLE_LOGGER_CHECKING
40static void
41logger(LY_LOG_LEVEL level, const char *msg, const char *path)
42{
43 (void) level; /* unused */
44 if (store) {
45 if (path && path[0]) {
46 snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
47 } else {
48 strncpy(logbuf, msg, BUFSIZE - 1);
49 }
50 if (store > 0) {
51 --store;
52 }
53 }
54}
55#endif
56
57static int
58setup(void **state)
59{
60 (void) state; /* unused */
61
62 const char *schema_a =
63 "module a {"
64 "namespace urn:tests:a;"
65 "prefix a;"
66 "yang-version 1.1;"
67
68 "list l1 {"
69 "key \"a b\";"
70 "leaf a {"
71 "type string;"
72 "}"
73 "leaf b {"
74 "type string;"
75 "}"
76 "leaf c {"
77 "type string;"
78 "}"
79 "}"
80 "leaf foo {"
81 "type string;"
82 "}"
83 "container c {"
84 "leaf x {"
85 "type string;"
86 "}"
87 "list ll {"
88 "key \"a\";"
89 "leaf a {"
90 "type string;"
91 "}"
92 "list ll {"
93 "key \"a\";"
94 "leaf a {"
95 "type string;"
96 "}"
97 "leaf b {"
98 "type string;"
99 "}"
100 "}"
101 "}"
Michal Vasko660cc8f2020-05-25 10:33:19 +0200102 "leaf-list ll2 {"
103 "type string;"
104 "}"
Michal Vasko14795a42020-05-22 16:44:44 +0200105 "}"
106 "}";
Michal Vasko61ac2f62020-05-25 12:39:51 +0200107 const char *schema_b =
108 "module b {"
109 "namespace urn:tests:b;"
110 "prefix b;"
111 "yang-version 1.1;"
112
113 "list l2 {"
114 "key \"a\";"
115 "leaf a {"
116 "type uint16;"
117 "}"
118 "leaf b {"
119 "type uint16;"
120 "}"
121 "}"
122 "}";
Michal Vasko14795a42020-05-22 16:44:44 +0200123
124#if ENABLE_LOGGER_CHECKING
125 ly_set_log_clb(logger, 1);
126#endif
127
128 assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200129 assert_int_equal(lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL), LY_SUCCESS);
130 assert_int_equal(lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL), LY_SUCCESS);
Michal Vasko14795a42020-05-22 16:44:44 +0200131
132 return 0;
133}
134
135static int
136teardown(void **state)
137{
138#if ENABLE_LOGGER_CHECKING
139 if (*state) {
140 fprintf(stderr, "%s\n", logbuf);
141 }
142#else
143 (void) state; /* unused */
144#endif
145
146 ly_ctx_destroy(ctx, NULL);
147 ctx = NULL;
148
149 return 0;
150}
151
152void
153logbuf_clean(void)
154{
155 logbuf[0] = '\0';
156}
157
158#if ENABLE_LOGGER_CHECKING
159# define logbuf_assert(str) assert_string_equal(logbuf, str)
160#else
161# define logbuf_assert(str)
162#endif
163
164static void
165test_hash(void **state)
166{
167 *state = test_hash;
168
169 const char *data =
170 "<l1 xmlns=\"urn:tests:a\">"
171 "<a>a1</a>"
172 "<b>b1</b>"
173 "<c>c1</c>"
174 "</l1>"
175 "<l1 xmlns=\"urn:tests:a\">"
176 "<a>a2</a>"
177 "<b>b2</b>"
178 "</l1>"
179 "<l1 xmlns=\"urn:tests:a\">"
180 "<a>a3</a>"
181 "<b>b3</b>"
182 "<c>c3</c>"
183 "</l1>"
184 "<foo xmlns=\"urn:tests:a\">foo value</foo>"
185 "<c xmlns=\"urn:tests:a\">"
186 "<x>val</x>"
187 "<ll>"
188 "<a>val_a</a>"
189 "<ll>"
190 "<a>val_a</a>"
191 "<b>val</b>"
192 "</ll>"
193 "<ll>"
194 "<a>val_b</a>"
195 "</ll>"
196 "</ll>"
197 "<ll>"
198 "<a>val_b</a>"
199 "<ll>"
200 "<a>val_a</a>"
201 "</ll>"
202 "<ll>"
203 "<a>val_b</a>"
204 "<b>val</b>"
205 "</ll>"
206 "</ll>"
207 "<ll>"
208 "<a>val_c</a>"
209 "<ll>"
210 "<a>val_a</a>"
211 "</ll>"
212 "<ll>"
213 "<a>val_b</a>"
214 "</ll>"
215 "</ll>"
Michal Vasko660cc8f2020-05-25 10:33:19 +0200216 "<ll2>one</ll2>"
217 "<ll2>two</ll2>"
218 "<ll2>three</ll2>"
219 "<ll2>four</ll2>"
Michal Vasko14795a42020-05-22 16:44:44 +0200220 "</c>";
221 struct lyd_node *tree, *node;
222 struct ly_set *set;
223 int dynamic;
224 const char *val_str;
225
Radek Krejci7931b192020-06-25 17:05:03 +0200226 assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
Michal Vasko14795a42020-05-22 16:44:44 +0200227 assert_non_null(tree);
228
229 /* top-level, so hash table is not ultimately used but instances can be compared based on hashes */
230 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a='a3'][b='b3']", &set));
231 assert_int_equal(1, set->count);
232
233 node = set->objs[0];
234 assert_string_equal(node->schema->name, "l1");
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200235 node = lyd_node_children(node, 0);
Michal Vasko14795a42020-05-22 16:44:44 +0200236 assert_string_equal(node->schema->name, "a");
237 val_str = lyd_value2str((struct lyd_node_term *)node, &dynamic);
238 assert_int_equal(0, dynamic);
239 assert_string_equal(val_str, "a3");
240
241 ly_set_free(set, NULL);
242
243 /* hashes should be used for both searches (well, there are not enough nested ll instances, so technically not true) */
244 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_b']/ll[a='val_b']", &set));
245 assert_int_equal(1, set->count);
246
247 node = set->objs[0];
248 assert_string_equal(node->schema->name, "ll");
Michal Vasko5bfd4be2020-06-23 13:26:19 +0200249 node = lyd_node_children(node, 0);
Michal Vasko14795a42020-05-22 16:44:44 +0200250 assert_string_equal(node->schema->name, "a");
251 val_str = lyd_value2str((struct lyd_node_term *)node, &dynamic);
252 assert_int_equal(0, dynamic);
253 assert_string_equal(val_str, "val_b");
254 node = node->next;
255 assert_string_equal(node->schema->name, "b");
256 assert_null(node->next);
257
258 ly_set_free(set, NULL);
259
260 /* hashes are not used */
261 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c//ll[a='val_b']", &set));
262 assert_int_equal(4, set->count);
263
264 ly_set_free(set, NULL);
265
Michal Vasko660cc8f2020-05-25 10:33:19 +0200266 /* hashes used even for leaf-lists */
267 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll2[. = 'three']", &set));
268 assert_int_equal(1, set->count);
269
270 node = set->objs[0];
271 assert_string_equal(node->schema->name, "ll2");
272 val_str = lyd_value2str((struct lyd_node_term *)node, &dynamic);
273 assert_int_equal(0, dynamic);
274 assert_string_equal(val_str, "three");
275
276 ly_set_free(set, NULL);
277
Michal Vasko14795a42020-05-22 16:44:44 +0200278 /* not found using hashes */
279 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_d']", &set));
280 assert_int_equal(0, set->count);
281
282 ly_set_free(set, NULL);
283
284 /* white-spaces are also ok */
285 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[ \na = 'val_c' ]", &set));
286 assert_int_equal(1, set->count);
287
288 ly_set_free(set, NULL);
289
290 lyd_free_all(tree);
291 *state = NULL;
292}
293
Michal Vasko61ac2f62020-05-25 12:39:51 +0200294static void
295test_toplevel(void **state)
296{
297 *state = test_toplevel;
298
299 const char *data =
300 "<l1 xmlns=\"urn:tests:a\">"
301 "<a>a1</a>"
302 "<b>b1</b>"
303 "<c>c1</c>"
304 "</l1>"
305 "<l1 xmlns=\"urn:tests:a\">"
306 "<a>a2</a>"
307 "<b>b2</b>"
308 "</l1>"
309 "<l1 xmlns=\"urn:tests:a\">"
310 "<a>a3</a>"
311 "<b>b3</b>"
312 "<c>c3</c>"
313 "</l1>"
314 "<foo xmlns=\"urn:tests:a\">foo value</foo>"
315 "<l2 xmlns=\"urn:tests:b\">"
316 "<a>1</a>"
317 "<b>1</b>"
318 "</l2>"
319 "<l2 xmlns=\"urn:tests:b\">"
320 "<a>2</a>"
321 "<b>1</b>"
322 "</l2>"
323 "<l2 xmlns=\"urn:tests:b\">"
324 "<a>3</a>"
325 "<b>1</b>"
326 "</l2>";
Michal Vaskof60e7102020-05-26 10:48:59 +0200327 struct lyd_node *tree;
Michal Vasko61ac2f62020-05-25 12:39:51 +0200328 struct ly_set *set;
Michal Vasko61ac2f62020-05-25 12:39:51 +0200329
Radek Krejci7931b192020-06-25 17:05:03 +0200330 assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_STRICT , LYD_VALIDATE_PRESENT, &tree));
Michal Vasko61ac2f62020-05-25 12:39:51 +0200331 assert_non_null(tree);
332
333 /* all top-level nodes from one module (default container as well) */
334 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:*", &set));
335 assert_int_equal(5, set->count);
336
337 ly_set_free(set, NULL);
338
339 /* all top-level nodes from all modules */
340 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/*", &set));
341 assert_int_equal(8, set->count);
342
343 ly_set_free(set, NULL);
344
345 /* all nodes from one module */
346 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//a:*", &set));
347 assert_int_equal(13, set->count);
348
349 ly_set_free(set, NULL);
350
351 /* all nodes from all modules */
352 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//*", &set));
353 assert_int_equal(22, set->count);
354
355 ly_set_free(set, NULL);
356
357 /* all nodes from all modules #2 */
358 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//.", &set));
359 assert_int_equal(22, set->count);
360
361 ly_set_free(set, NULL);
362
363 lyd_free_all(tree);
364 *state = NULL;
365}
366
Michal Vasko519fd602020-05-26 12:17:39 +0200367static void
368test_atomize(void **state)
369{
370 *state = test_atomize;
371
372 struct ly_set *set;
373 const struct lys_module *mod;
374
375 mod = ly_ctx_get_module_latest(ctx, "a");
376
377 /* some random paths just making sure the API function works */
378 assert_int_equal(LY_SUCCESS, lys_atomize_xpath(mod->compiled->data, "/a:*", 0, &set));
379 assert_int_equal(3, set->count);
380
381 ly_set_free(set, NULL);
382
383 /* all nodes from all modules (including internal, which can change easily, so check just the test modules) */
384 assert_int_equal(LY_SUCCESS, lys_atomize_xpath(mod->compiled->data, "//.", 0, &set));
385 assert_in_range(set->count, 16, UINT32_MAX);
386
387 ly_set_free(set, NULL);
388
389 assert_int_equal(LY_SUCCESS, lys_atomize_xpath(mod->compiled->data->next->next, "/a:c/ll[a='val1']/ll[a='val2']/b", 0, &set));
390 assert_int_equal(6, set->count);
391
392 ly_set_free(set, NULL);
393}
394
Michal Vasko14795a42020-05-22 16:44:44 +0200395int main(void)
396{
397 const struct CMUnitTest tests[] = {
398 cmocka_unit_test_setup_teardown(test_hash, setup, teardown),
Michal Vasko61ac2f62020-05-25 12:39:51 +0200399 cmocka_unit_test_setup_teardown(test_toplevel, setup, teardown),
Michal Vasko519fd602020-05-26 12:17:39 +0200400 cmocka_unit_test_setup_teardown(test_atomize, setup, teardown),
Michal Vasko14795a42020-05-22 16:44:44 +0200401 };
402
403 return cmocka_run_group_tests(tests, NULL, NULL);
404}