blob: 8f1b5331bfef1485b85ac08cddd20985304a86d1 [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
15#include "tests/config.h"
16
17#include <stdarg.h>
18#include <stddef.h>
19#include <setjmp.h>
20#include <cmocka.h>
21
22#include <stdio.h>
23#include <string.h>
24
25#include "../../src/context.h"
26#include "../../src/tree_data.h"
Michal Vasko519fd602020-05-26 12:17:39 +020027#include "../../src/tree_schema.h"
Michal Vasko14795a42020-05-22 16:44:44 +020028
29#define BUFSIZE 1024
30char logbuf[BUFSIZE] = {0};
31int store = -1; /* negative for infinite logging, positive for limited logging */
32
33struct ly_ctx *ctx; /* context for tests */
34
35/* set to 0 to printing error messages to stderr instead of checking them in code */
36#define ENABLE_LOGGER_CHECKING 1
37
38#if ENABLE_LOGGER_CHECKING
39static void
40logger(LY_LOG_LEVEL level, const char *msg, const char *path)
41{
42 (void) level; /* unused */
43 if (store) {
44 if (path && path[0]) {
45 snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
46 } else {
47 strncpy(logbuf, msg, BUFSIZE - 1);
48 }
49 if (store > 0) {
50 --store;
51 }
52 }
53}
54#endif
55
56static int
57setup(void **state)
58{
59 (void) state; /* unused */
60
61 const char *schema_a =
62 "module a {"
63 "namespace urn:tests:a;"
64 "prefix a;"
65 "yang-version 1.1;"
66
67 "list l1 {"
68 "key \"a b\";"
69 "leaf a {"
70 "type string;"
71 "}"
72 "leaf b {"
73 "type string;"
74 "}"
75 "leaf c {"
76 "type string;"
77 "}"
78 "}"
79 "leaf foo {"
80 "type string;"
81 "}"
82 "container c {"
83 "leaf x {"
84 "type string;"
85 "}"
86 "list ll {"
87 "key \"a\";"
88 "leaf a {"
89 "type string;"
90 "}"
91 "list ll {"
92 "key \"a\";"
93 "leaf a {"
94 "type string;"
95 "}"
96 "leaf b {"
97 "type string;"
98 "}"
99 "}"
100 "}"
Michal Vasko660cc8f2020-05-25 10:33:19 +0200101 "leaf-list ll2 {"
102 "type string;"
103 "}"
Michal Vasko14795a42020-05-22 16:44:44 +0200104 "}"
105 "}";
Michal Vasko61ac2f62020-05-25 12:39:51 +0200106 const char *schema_b =
107 "module b {"
108 "namespace urn:tests:b;"
109 "prefix b;"
110 "yang-version 1.1;"
111
112 "list l2 {"
113 "key \"a\";"
114 "leaf a {"
115 "type uint16;"
116 "}"
117 "leaf b {"
118 "type uint16;"
119 "}"
120 "}"
121 "}";
Michal Vasko14795a42020-05-22 16:44:44 +0200122
123#if ENABLE_LOGGER_CHECKING
124 ly_set_log_clb(logger, 1);
125#endif
126
127 assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
128 assert_non_null(lys_parse_mem(ctx, schema_a, LYS_IN_YANG));
Michal Vasko61ac2f62020-05-25 12:39:51 +0200129 assert_non_null(lys_parse_mem(ctx, schema_b, LYS_IN_YANG));
Michal Vasko14795a42020-05-22 16:44:44 +0200130
131 return 0;
132}
133
134static int
135teardown(void **state)
136{
137#if ENABLE_LOGGER_CHECKING
138 if (*state) {
139 fprintf(stderr, "%s\n", logbuf);
140 }
141#else
142 (void) state; /* unused */
143#endif
144
145 ly_ctx_destroy(ctx, NULL);
146 ctx = NULL;
147
148 return 0;
149}
150
151void
152logbuf_clean(void)
153{
154 logbuf[0] = '\0';
155}
156
157#if ENABLE_LOGGER_CHECKING
158# define logbuf_assert(str) assert_string_equal(logbuf, str)
159#else
160# define logbuf_assert(str)
161#endif
162
163static void
164test_hash(void **state)
165{
166 *state = test_hash;
167
168 const char *data =
169 "<l1 xmlns=\"urn:tests:a\">"
170 "<a>a1</a>"
171 "<b>b1</b>"
172 "<c>c1</c>"
173 "</l1>"
174 "<l1 xmlns=\"urn:tests:a\">"
175 "<a>a2</a>"
176 "<b>b2</b>"
177 "</l1>"
178 "<l1 xmlns=\"urn:tests:a\">"
179 "<a>a3</a>"
180 "<b>b3</b>"
181 "<c>c3</c>"
182 "</l1>"
183 "<foo xmlns=\"urn:tests:a\">foo value</foo>"
184 "<c xmlns=\"urn:tests:a\">"
185 "<x>val</x>"
186 "<ll>"
187 "<a>val_a</a>"
188 "<ll>"
189 "<a>val_a</a>"
190 "<b>val</b>"
191 "</ll>"
192 "<ll>"
193 "<a>val_b</a>"
194 "</ll>"
195 "</ll>"
196 "<ll>"
197 "<a>val_b</a>"
198 "<ll>"
199 "<a>val_a</a>"
200 "</ll>"
201 "<ll>"
202 "<a>val_b</a>"
203 "<b>val</b>"
204 "</ll>"
205 "</ll>"
206 "<ll>"
207 "<a>val_c</a>"
208 "<ll>"
209 "<a>val_a</a>"
210 "</ll>"
211 "<ll>"
212 "<a>val_b</a>"
213 "</ll>"
214 "</ll>"
Michal Vasko660cc8f2020-05-25 10:33:19 +0200215 "<ll2>one</ll2>"
216 "<ll2>two</ll2>"
217 "<ll2>three</ll2>"
218 "<ll2>four</ll2>"
Michal Vasko14795a42020-05-22 16:44:44 +0200219 "</c>";
220 struct lyd_node *tree, *node;
221 struct ly_set *set;
222 int dynamic;
223 const char *val_str;
224
225 tree = lyd_parse_mem(ctx, data, LYD_XML, LYD_OPT_STRICT | LYD_VALOPT_DATA_ONLY);
226 assert_non_null(tree);
227
228 /* top-level, so hash table is not ultimately used but instances can be compared based on hashes */
229 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a='a3'][b='b3']", &set));
230 assert_int_equal(1, set->count);
231
232 node = set->objs[0];
233 assert_string_equal(node->schema->name, "l1");
234 node = lyd_node_children(node);
235 assert_string_equal(node->schema->name, "a");
236 val_str = lyd_value2str((struct lyd_node_term *)node, &dynamic);
237 assert_int_equal(0, dynamic);
238 assert_string_equal(val_str, "a3");
239
240 ly_set_free(set, NULL);
241
242 /* hashes should be used for both searches (well, there are not enough nested ll instances, so technically not true) */
243 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_b']/ll[a='val_b']", &set));
244 assert_int_equal(1, set->count);
245
246 node = set->objs[0];
247 assert_string_equal(node->schema->name, "ll");
248 node = lyd_node_children(node);
249 assert_string_equal(node->schema->name, "a");
250 val_str = lyd_value2str((struct lyd_node_term *)node, &dynamic);
251 assert_int_equal(0, dynamic);
252 assert_string_equal(val_str, "val_b");
253 node = node->next;
254 assert_string_equal(node->schema->name, "b");
255 assert_null(node->next);
256
257 ly_set_free(set, NULL);
258
259 /* hashes are not used */
260 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c//ll[a='val_b']", &set));
261 assert_int_equal(4, set->count);
262
263 ly_set_free(set, NULL);
264
Michal Vasko660cc8f2020-05-25 10:33:19 +0200265 /* hashes used even for leaf-lists */
266 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll2[. = 'three']", &set));
267 assert_int_equal(1, set->count);
268
269 node = set->objs[0];
270 assert_string_equal(node->schema->name, "ll2");
271 val_str = lyd_value2str((struct lyd_node_term *)node, &dynamic);
272 assert_int_equal(0, dynamic);
273 assert_string_equal(val_str, "three");
274
275 ly_set_free(set, NULL);
276
Michal Vasko14795a42020-05-22 16:44:44 +0200277 /* not found using hashes */
278 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_d']", &set));
279 assert_int_equal(0, set->count);
280
281 ly_set_free(set, NULL);
282
283 /* white-spaces are also ok */
284 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[ \na = 'val_c' ]", &set));
285 assert_int_equal(1, set->count);
286
287 ly_set_free(set, NULL);
288
289 lyd_free_all(tree);
290 *state = NULL;
291}
292
Michal Vasko61ac2f62020-05-25 12:39:51 +0200293static void
294test_toplevel(void **state)
295{
296 *state = test_toplevel;
297
298 const char *data =
299 "<l1 xmlns=\"urn:tests:a\">"
300 "<a>a1</a>"
301 "<b>b1</b>"
302 "<c>c1</c>"
303 "</l1>"
304 "<l1 xmlns=\"urn:tests:a\">"
305 "<a>a2</a>"
306 "<b>b2</b>"
307 "</l1>"
308 "<l1 xmlns=\"urn:tests:a\">"
309 "<a>a3</a>"
310 "<b>b3</b>"
311 "<c>c3</c>"
312 "</l1>"
313 "<foo xmlns=\"urn:tests:a\">foo value</foo>"
314 "<l2 xmlns=\"urn:tests:b\">"
315 "<a>1</a>"
316 "<b>1</b>"
317 "</l2>"
318 "<l2 xmlns=\"urn:tests:b\">"
319 "<a>2</a>"
320 "<b>1</b>"
321 "</l2>"
322 "<l2 xmlns=\"urn:tests:b\">"
323 "<a>3</a>"
324 "<b>1</b>"
325 "</l2>";
Michal Vaskof60e7102020-05-26 10:48:59 +0200326 struct lyd_node *tree;
Michal Vasko61ac2f62020-05-25 12:39:51 +0200327 struct ly_set *set;
Michal Vasko61ac2f62020-05-25 12:39:51 +0200328
329 tree = lyd_parse_mem(ctx, data, LYD_XML, LYD_OPT_STRICT | LYD_VALOPT_DATA_ONLY);
330 assert_non_null(tree);
331
332 /* all top-level nodes from one module (default container as well) */
333 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:*", &set));
334 assert_int_equal(5, set->count);
335
336 ly_set_free(set, NULL);
337
338 /* all top-level nodes from all modules */
339 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/*", &set));
340 assert_int_equal(8, set->count);
341
342 ly_set_free(set, NULL);
343
344 /* all nodes from one module */
345 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//a:*", &set));
346 assert_int_equal(13, set->count);
347
348 ly_set_free(set, NULL);
349
350 /* all nodes from all modules */
351 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//*", &set));
352 assert_int_equal(22, set->count);
353
354 ly_set_free(set, NULL);
355
356 /* all nodes from all modules #2 */
357 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//.", &set));
358 assert_int_equal(22, set->count);
359
360 ly_set_free(set, NULL);
361
362 lyd_free_all(tree);
363 *state = NULL;
364}
365
Michal Vasko519fd602020-05-26 12:17:39 +0200366static void
367test_atomize(void **state)
368{
369 *state = test_atomize;
370
371 struct ly_set *set;
372 const struct lys_module *mod;
373
374 mod = ly_ctx_get_module_latest(ctx, "a");
375
376 /* some random paths just making sure the API function works */
377 assert_int_equal(LY_SUCCESS, lys_atomize_xpath(mod->compiled->data, "/a:*", 0, &set));
378 assert_int_equal(3, set->count);
379
380 ly_set_free(set, NULL);
381
382 /* all nodes from all modules (including internal, which can change easily, so check just the test modules) */
383 assert_int_equal(LY_SUCCESS, lys_atomize_xpath(mod->compiled->data, "//.", 0, &set));
384 assert_in_range(set->count, 16, UINT32_MAX);
385
386 ly_set_free(set, NULL);
387
388 assert_int_equal(LY_SUCCESS, lys_atomize_xpath(mod->compiled->data->next->next, "/a:c/ll[a='val1']/ll[a='val2']/b", 0, &set));
389 assert_int_equal(6, set->count);
390
391 ly_set_free(set, NULL);
392}
393
Michal Vasko14795a42020-05-22 16:44:44 +0200394int main(void)
395{
396 const struct CMUnitTest tests[] = {
397 cmocka_unit_test_setup_teardown(test_hash, setup, teardown),
Michal Vasko61ac2f62020-05-25 12:39:51 +0200398 cmocka_unit_test_setup_teardown(test_toplevel, setup, teardown),
Michal Vasko519fd602020-05-26 12:17:39 +0200399 cmocka_unit_test_setup_teardown(test_atomize, setup, teardown),
Michal Vasko14795a42020-05-22 16:44:44 +0200400 };
401
402 return cmocka_run_group_tests(tests, NULL, NULL);
403}