blob: 637476d49d3db3af1ccc50ca1da1a50db846cecf [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 "}"
Michal Vasko4c7763f2020-07-27 17:40:37 +020083 "leaf foo2 {"
84 "type uint8;"
85 "}"
Michal Vasko14795a42020-05-22 16:44:44 +020086 "container c {"
87 "leaf x {"
88 "type string;"
89 "}"
90 "list ll {"
91 "key \"a\";"
92 "leaf a {"
93 "type string;"
94 "}"
95 "list ll {"
96 "key \"a\";"
97 "leaf a {"
98 "type string;"
99 "}"
100 "leaf b {"
101 "type string;"
102 "}"
103 "}"
104 "}"
Michal Vasko660cc8f2020-05-25 10:33:19 +0200105 "leaf-list ll2 {"
106 "type string;"
107 "}"
Michal Vasko14795a42020-05-22 16:44:44 +0200108 "}"
109 "}";
Michal Vasko61ac2f62020-05-25 12:39:51 +0200110 const char *schema_b =
111 "module b {"
112 "namespace urn:tests:b;"
113 "prefix b;"
114 "yang-version 1.1;"
115
116 "list l2 {"
117 "key \"a\";"
118 "leaf a {"
119 "type uint16;"
120 "}"
121 "leaf b {"
122 "type uint16;"
123 "}"
124 "}"
125 "}";
Michal Vasko14795a42020-05-22 16:44:44 +0200126
127#if ENABLE_LOGGER_CHECKING
128 ly_set_log_clb(logger, 1);
129#endif
130
131 assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200132 assert_int_equal(lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL), LY_SUCCESS);
133 assert_int_equal(lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL), LY_SUCCESS);
Michal Vasko14795a42020-05-22 16:44:44 +0200134
135 return 0;
136}
137
138static int
139teardown(void **state)
140{
141#if ENABLE_LOGGER_CHECKING
142 if (*state) {
143 fprintf(stderr, "%s\n", logbuf);
144 }
145#else
146 (void) state; /* unused */
147#endif
148
149 ly_ctx_destroy(ctx, NULL);
150 ctx = NULL;
151
152 return 0;
153}
154
155void
156logbuf_clean(void)
157{
158 logbuf[0] = '\0';
159}
160
161#if ENABLE_LOGGER_CHECKING
162# define logbuf_assert(str) assert_string_equal(logbuf, str)
163#else
164# define logbuf_assert(str)
165#endif
166
167static void
168test_hash(void **state)
169{
170 *state = test_hash;
171
172 const char *data =
173 "<l1 xmlns=\"urn:tests:a\">"
174 "<a>a1</a>"
175 "<b>b1</b>"
176 "<c>c1</c>"
177 "</l1>"
178 "<l1 xmlns=\"urn:tests:a\">"
179 "<a>a2</a>"
180 "<b>b2</b>"
181 "</l1>"
182 "<l1 xmlns=\"urn:tests:a\">"
183 "<a>a3</a>"
184 "<b>b3</b>"
185 "<c>c3</c>"
186 "</l1>"
187 "<foo xmlns=\"urn:tests:a\">foo value</foo>"
188 "<c xmlns=\"urn:tests:a\">"
189 "<x>val</x>"
190 "<ll>"
191 "<a>val_a</a>"
192 "<ll>"
193 "<a>val_a</a>"
194 "<b>val</b>"
195 "</ll>"
196 "<ll>"
197 "<a>val_b</a>"
198 "</ll>"
199 "</ll>"
200 "<ll>"
201 "<a>val_b</a>"
202 "<ll>"
203 "<a>val_a</a>"
204 "</ll>"
205 "<ll>"
206 "<a>val_b</a>"
207 "<b>val</b>"
208 "</ll>"
209 "</ll>"
210 "<ll>"
211 "<a>val_c</a>"
212 "<ll>"
213 "<a>val_a</a>"
214 "</ll>"
215 "<ll>"
216 "<a>val_b</a>"
217 "</ll>"
218 "</ll>"
Michal Vasko660cc8f2020-05-25 10:33:19 +0200219 "<ll2>one</ll2>"
220 "<ll2>two</ll2>"
221 "<ll2>three</ll2>"
222 "<ll2>four</ll2>"
Michal Vasko14795a42020-05-22 16:44:44 +0200223 "</c>";
224 struct lyd_node *tree, *node;
225 struct ly_set *set;
Michal Vasko14795a42020-05-22 16:44:44 +0200226
Radek Krejci7931b192020-06-25 17:05:03 +0200227 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 +0200228 assert_non_null(tree);
229
230 /* top-level, so hash table is not ultimately used but instances can be compared based on hashes */
231 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:l1[a='a3'][b='b3']", &set));
232 assert_int_equal(1, set->count);
233
234 node = set->objs[0];
235 assert_string_equal(node->schema->name, "l1");
Radek Krejcia1c1e542020-09-29 16:06:52 +0200236 node = lyd_child(node);
Michal Vasko14795a42020-05-22 16:44:44 +0200237 assert_string_equal(node->schema->name, "a");
Michal Vaskob7be7a82020-08-20 09:09:04 +0200238 assert_string_equal(LYD_CANON_VALUE(node), "a3");
Michal Vasko14795a42020-05-22 16:44:44 +0200239
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");
Radek Krejcia1c1e542020-09-29 16:06:52 +0200248 node = lyd_child(node);
Michal Vasko14795a42020-05-22 16:44:44 +0200249 assert_string_equal(node->schema->name, "a");
Michal Vaskob7be7a82020-08-20 09:09:04 +0200250 assert_string_equal(LYD_CANON_VALUE(node), "val_b");
Michal Vasko14795a42020-05-22 16:44:44 +0200251 node = node->next;
252 assert_string_equal(node->schema->name, "b");
253 assert_null(node->next);
254
255 ly_set_free(set, NULL);
256
257 /* hashes are not used */
258 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c//ll[a='val_b']", &set));
259 assert_int_equal(4, set->count);
260
261 ly_set_free(set, NULL);
262
Michal Vasko660cc8f2020-05-25 10:33:19 +0200263 /* hashes used even for leaf-lists */
264 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll2[. = 'three']", &set));
265 assert_int_equal(1, set->count);
266
267 node = set->objs[0];
268 assert_string_equal(node->schema->name, "ll2");
Michal Vaskob7be7a82020-08-20 09:09:04 +0200269 assert_string_equal(LYD_CANON_VALUE(node), "three");
Michal Vasko660cc8f2020-05-25 10:33:19 +0200270
271 ly_set_free(set, NULL);
272
Michal Vasko14795a42020-05-22 16:44:44 +0200273 /* not found using hashes */
274 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[a='val_d']", &set));
275 assert_int_equal(0, set->count);
276
277 ly_set_free(set, NULL);
278
279 /* white-spaces are also ok */
280 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:c/ll[ \na = 'val_c' ]", &set));
281 assert_int_equal(1, set->count);
282
283 ly_set_free(set, NULL);
284
285 lyd_free_all(tree);
286 *state = NULL;
287}
288
Michal Vasko61ac2f62020-05-25 12:39:51 +0200289static void
290test_toplevel(void **state)
291{
292 *state = test_toplevel;
293
294 const char *data =
295 "<l1 xmlns=\"urn:tests:a\">"
296 "<a>a1</a>"
297 "<b>b1</b>"
298 "<c>c1</c>"
299 "</l1>"
300 "<l1 xmlns=\"urn:tests:a\">"
301 "<a>a2</a>"
302 "<b>b2</b>"
303 "</l1>"
304 "<l1 xmlns=\"urn:tests:a\">"
305 "<a>a3</a>"
306 "<b>b3</b>"
307 "<c>c3</c>"
308 "</l1>"
309 "<foo xmlns=\"urn:tests:a\">foo value</foo>"
310 "<l2 xmlns=\"urn:tests:b\">"
311 "<a>1</a>"
312 "<b>1</b>"
313 "</l2>"
314 "<l2 xmlns=\"urn:tests:b\">"
315 "<a>2</a>"
316 "<b>1</b>"
317 "</l2>"
318 "<l2 xmlns=\"urn:tests:b\">"
319 "<a>3</a>"
320 "<b>1</b>"
321 "</l2>";
Michal Vaskof60e7102020-05-26 10:48:59 +0200322 struct lyd_node *tree;
Michal Vasko61ac2f62020-05-25 12:39:51 +0200323 struct ly_set *set;
Michal Vasko61ac2f62020-05-25 12:39:51 +0200324
Radek Krejci7931b192020-06-25 17:05:03 +0200325 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 +0200326 assert_non_null(tree);
327
328 /* all top-level nodes from one module (default container as well) */
329 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:*", &set));
330 assert_int_equal(5, set->count);
331
332 ly_set_free(set, NULL);
333
334 /* all top-level nodes from all modules */
335 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/*", &set));
336 assert_int_equal(8, set->count);
337
338 ly_set_free(set, NULL);
339
340 /* all nodes from one module */
341 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//a:*", &set));
342 assert_int_equal(13, set->count);
343
344 ly_set_free(set, NULL);
345
346 /* all nodes from all modules */
347 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//*", &set));
348 assert_int_equal(22, set->count);
349
350 ly_set_free(set, NULL);
351
352 /* all nodes from all modules #2 */
353 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "//.", &set));
354 assert_int_equal(22, set->count);
355
356 ly_set_free(set, NULL);
357
358 lyd_free_all(tree);
359 *state = NULL;
360}
361
Michal Vasko519fd602020-05-26 12:17:39 +0200362static void
363test_atomize(void **state)
364{
365 *state = test_atomize;
366
367 struct ly_set *set;
368 const struct lys_module *mod;
369
370 mod = ly_ctx_get_module_latest(ctx, "a");
371
372 /* some random paths just making sure the API function works */
Radek Krejcibed13942020-10-19 16:06:28 +0200373 assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(mod->compiled->data, "/a:*", 0, &set));
Michal Vasko4c7763f2020-07-27 17:40:37 +0200374 assert_int_equal(4, set->count);
Michal Vasko519fd602020-05-26 12:17:39 +0200375
376 ly_set_free(set, NULL);
377
378 /* all nodes from all modules (including internal, which can change easily, so check just the test modules) */
Radek Krejcibed13942020-10-19 16:06:28 +0200379 assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(mod->compiled->data, "//.", 0, &set));
Michal Vasko519fd602020-05-26 12:17:39 +0200380 assert_in_range(set->count, 16, UINT32_MAX);
381
382 ly_set_free(set, NULL);
383
Radek Krejcibed13942020-10-19 16:06:28 +0200384 assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(mod->compiled->data->next->next, "/a:c/ll[a='val1']/ll[a='val2']/b",
Michal Vasko4c7763f2020-07-27 17:40:37 +0200385 0, &set));
386 assert_int_equal(7, set->count);
Michal Vasko519fd602020-05-26 12:17:39 +0200387
388 ly_set_free(set, NULL);
389}
390
Michal Vasko4c7763f2020-07-27 17:40:37 +0200391static void
392test_canonize(void **state)
393{
394 *state = test_canonize;
395
396 const char *data =
397 "<foo2 xmlns=\"urn:tests:a\">50</foo2>";
398 struct lyd_node *tree;
399 struct ly_set *set;
400
401 assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
402 assert_non_null(tree);
403
404 assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:foo2[.='050']", &set));
405 assert_int_equal(1, set->count);
406 ly_set_free(set, NULL);
407
408 /* TODO more use-cases once there are some type plugins that have canonical values */
409
410 lyd_free_all(tree);
411 *state = NULL;
412}
413
Michal Vasko14795a42020-05-22 16:44:44 +0200414int main(void)
415{
416 const struct CMUnitTest tests[] = {
417 cmocka_unit_test_setup_teardown(test_hash, setup, teardown),
Michal Vasko61ac2f62020-05-25 12:39:51 +0200418 cmocka_unit_test_setup_teardown(test_toplevel, setup, teardown),
Michal Vasko519fd602020-05-26 12:17:39 +0200419 cmocka_unit_test_setup_teardown(test_atomize, setup, teardown),
Michal Vasko4c7763f2020-07-27 17:40:37 +0200420 cmocka_unit_test_setup_teardown(test_canonize, setup, teardown),
Michal Vasko14795a42020-05-22 16:44:44 +0200421 };
422
423 return cmocka_run_group_tests(tests, NULL, NULL);
424}