]> git.proxmox.com Git - mirror_qemu.git/blame - tests/check-block-qdict.c
check-block-qdict: Cover flattening of empty lists and dictionaries
[mirror_qemu.git] / tests / check-block-qdict.c
CommitLineData
0bcc8e5b
MA
1/*
2 * Unit-tests for Block layer QDict extras
3 *
4 * Copyright (c) 2013-2018 Red Hat, Inc.
5 *
6 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
7 * See the COPYING.LIB file in the top-level directory.
8 */
9
10#include "qemu/osdep.h"
11#include "block/qdict.h"
12#include "qapi/qmp/qlist.h"
13#include "qapi/qmp/qnum.h"
14#include "qapi/error.h"
15
16static void qdict_defaults_test(void)
17{
18 QDict *dict, *copy;
19
20 dict = qdict_new();
21 copy = qdict_new();
22
23 qdict_set_default_str(dict, "foo", "abc");
24 qdict_set_default_str(dict, "foo", "def");
25 g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc");
26 qdict_set_default_str(dict, "bar", "ghi");
27
28 qdict_copy_default(copy, dict, "foo");
29 g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc");
30 qdict_set_default_str(copy, "bar", "xyz");
31 qdict_copy_default(copy, dict, "bar");
32 g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz");
33
34 qobject_unref(copy);
35 qobject_unref(dict);
36}
37
38static void qdict_flatten_test(void)
39{
cddec036
MA
40 QList *e_1 = qlist_new();
41 QList *e = qlist_new();
42 QDict *e_1_2 = qdict_new();
43 QDict *f = qdict_new();
bef96b15
MA
44 QList *y = qlist_new();
45 QDict *z = qdict_new();
cddec036 46 QDict *root = qdict_new();
0bcc8e5b
MA
47
48 /*
49 * Test the flattening of
50 *
51 * {
52 * "e": [
53 * 42,
54 * [
55 * 23,
56 * 66,
57 * {
58 * "a": 0,
59 * "b": 1
60 * }
61 * ]
62 * ],
63 * "f": {
64 * "c": 2,
65 * "d": 3,
66 * },
bef96b15
MA
67 * "g": 4,
68 * "y": [{}],
69 * "z": {"a": []}
0bcc8e5b
MA
70 * }
71 *
72 * to
73 *
74 * {
75 * "e.0": 42,
76 * "e.1.0": 23,
77 * "e.1.1": 66,
78 * "e.1.2.a": 0,
79 * "e.1.2.b": 1,
80 * "f.c": 2,
81 * "f.d": 3,
82 * "g": 4
83 * }
bef96b15
MA
84 *
85 * Note that "y" and "z" get eaten.
0bcc8e5b
MA
86 */
87
cddec036
MA
88 qdict_put_int(e_1_2, "a", 0);
89 qdict_put_int(e_1_2, "b", 1);
0bcc8e5b 90
cddec036
MA
91 qlist_append_int(e_1, 23);
92 qlist_append_int(e_1, 66);
93 qlist_append(e_1, e_1_2);
94 qlist_append_int(e, 42);
95 qlist_append(e, e_1);
0bcc8e5b 96
cddec036
MA
97 qdict_put_int(f, "c", 2);
98 qdict_put_int(f, "d", 3);
0bcc8e5b 99
bef96b15
MA
100 qlist_append(y, qdict_new());
101
102 qdict_put(z, "a", qlist_new());
103
cddec036
MA
104 qdict_put(root, "e", e);
105 qdict_put(root, "f", f);
106 qdict_put_int(root, "g", 4);
bef96b15
MA
107 qdict_put(root, "y", y);
108 qdict_put(root, "z", z);
0bcc8e5b 109
cddec036 110 qdict_flatten(root);
0bcc8e5b 111
cddec036
MA
112 g_assert(qdict_get_int(root, "e.0") == 42);
113 g_assert(qdict_get_int(root, "e.1.0") == 23);
114 g_assert(qdict_get_int(root, "e.1.1") == 66);
115 g_assert(qdict_get_int(root, "e.1.2.a") == 0);
116 g_assert(qdict_get_int(root, "e.1.2.b") == 1);
117 g_assert(qdict_get_int(root, "f.c") == 2);
118 g_assert(qdict_get_int(root, "f.d") == 3);
119 g_assert(qdict_get_int(root, "g") == 4);
0bcc8e5b 120
cddec036
MA
121 g_assert(qdict_size(root) == 8);
122
123 qobject_unref(root);
0bcc8e5b
MA
124}
125
126static void qdict_array_split_test(void)
127{
128 QDict *test_dict = qdict_new();
129 QDict *dict1, *dict2;
130 QNum *int1;
131 QList *test_list;
132
133 /*
134 * Test the split of
135 *
136 * {
137 * "1.x": 0,
138 * "4.y": 1,
139 * "0.a": 42,
140 * "o.o": 7,
141 * "0.b": 23,
142 * "2": 66
143 * }
144 *
145 * to
146 *
147 * [
148 * {
149 * "a": 42,
150 * "b": 23
151 * },
152 * {
153 * "x": 0
154 * },
155 * 66
156 * ]
157 *
158 * and
159 *
160 * {
161 * "4.y": 1,
162 * "o.o": 7
163 * }
164 *
165 * (remaining in the old QDict)
166 *
167 * This example is given in the comment of qdict_array_split().
168 */
169
170 qdict_put_int(test_dict, "1.x", 0);
171 qdict_put_int(test_dict, "4.y", 1);
172 qdict_put_int(test_dict, "0.a", 42);
173 qdict_put_int(test_dict, "o.o", 7);
174 qdict_put_int(test_dict, "0.b", 23);
175 qdict_put_int(test_dict, "2", 66);
176
177 qdict_array_split(test_dict, &test_list);
178
179 dict1 = qobject_to(QDict, qlist_pop(test_list));
180 dict2 = qobject_to(QDict, qlist_pop(test_list));
181 int1 = qobject_to(QNum, qlist_pop(test_list));
182
183 g_assert(dict1);
184 g_assert(dict2);
185 g_assert(int1);
186 g_assert(qlist_empty(test_list));
187
188 qobject_unref(test_list);
189
190 g_assert(qdict_get_int(dict1, "a") == 42);
191 g_assert(qdict_get_int(dict1, "b") == 23);
192
193 g_assert(qdict_size(dict1) == 2);
194
195 qobject_unref(dict1);
196
197 g_assert(qdict_get_int(dict2, "x") == 0);
198
199 g_assert(qdict_size(dict2) == 1);
200
201 qobject_unref(dict2);
202
203 g_assert_cmpint(qnum_get_int(int1), ==, 66);
204
205 qobject_unref(int1);
206
207 g_assert(qdict_get_int(test_dict, "4.y") == 1);
208 g_assert(qdict_get_int(test_dict, "o.o") == 7);
209
210 g_assert(qdict_size(test_dict) == 2);
211
212 qobject_unref(test_dict);
213
214 /*
215 * Test the split of
216 *
217 * {
218 * "0": 42,
219 * "1": 23,
220 * "1.x": 84
221 * }
222 *
223 * to
224 *
225 * [
226 * 42
227 * ]
228 *
229 * and
230 *
231 * {
232 * "1": 23,
233 * "1.x": 84
234 * }
235 *
236 * That is, test whether splitting stops if there is both an entry with key
237 * of "%u" and other entries with keys prefixed "%u." for the same index.
238 */
239
240 test_dict = qdict_new();
241
242 qdict_put_int(test_dict, "0", 42);
243 qdict_put_int(test_dict, "1", 23);
244 qdict_put_int(test_dict, "1.x", 84);
245
246 qdict_array_split(test_dict, &test_list);
247
248 int1 = qobject_to(QNum, qlist_pop(test_list));
249
250 g_assert(int1);
251 g_assert(qlist_empty(test_list));
252
253 qobject_unref(test_list);
254
255 g_assert_cmpint(qnum_get_int(int1), ==, 42);
256
257 qobject_unref(int1);
258
259 g_assert(qdict_get_int(test_dict, "1") == 23);
260 g_assert(qdict_get_int(test_dict, "1.x") == 84);
261
262 g_assert(qdict_size(test_dict) == 2);
263
264 qobject_unref(test_dict);
265}
266
267static void qdict_array_entries_test(void)
268{
269 QDict *dict = qdict_new();
270
271 g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
272
273 qdict_put_int(dict, "bar", 0);
274 qdict_put_int(dict, "baz.0", 0);
275 g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0);
276
277 qdict_put_int(dict, "foo.1", 0);
278 g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
279 qdict_put_int(dict, "foo.0", 0);
280 g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2);
281 qdict_put_int(dict, "foo.bar", 0);
282 g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL);
283 qdict_del(dict, "foo.bar");
284
285 qdict_put_int(dict, "foo.2.a", 0);
286 qdict_put_int(dict, "foo.2.b", 0);
287 qdict_put_int(dict, "foo.2.c", 0);
288 g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3);
289 g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
290
291 qobject_unref(dict);
292
293 dict = qdict_new();
294 qdict_put_int(dict, "1", 0);
295 g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
296 qdict_put_int(dict, "0", 0);
297 g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2);
298 qdict_put_int(dict, "bar", 0);
299 g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL);
300 qdict_del(dict, "bar");
301
302 qdict_put_int(dict, "2.a", 0);
303 qdict_put_int(dict, "2.b", 0);
304 qdict_put_int(dict, "2.c", 0);
305 g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3);
306
307 qobject_unref(dict);
308}
309
310static void qdict_join_test(void)
311{
312 QDict *dict1, *dict2;
313 bool overwrite = false;
314 int i;
315
316 dict1 = qdict_new();
317 dict2 = qdict_new();
318
319 /* Test everything once without overwrite and once with */
320 do {
321 /* Test empty dicts */
322 qdict_join(dict1, dict2, overwrite);
323
324 g_assert(qdict_size(dict1) == 0);
325 g_assert(qdict_size(dict2) == 0);
326
327 /* First iteration: Test movement */
328 /* Second iteration: Test empty source and non-empty destination */
329 qdict_put_int(dict2, "foo", 42);
330
331 for (i = 0; i < 2; i++) {
332 qdict_join(dict1, dict2, overwrite);
333
334 g_assert(qdict_size(dict1) == 1);
335 g_assert(qdict_size(dict2) == 0);
336
337 g_assert(qdict_get_int(dict1, "foo") == 42);
338 }
339
340 /* Test non-empty source and destination without conflict */
341 qdict_put_int(dict2, "bar", 23);
342
343 qdict_join(dict1, dict2, overwrite);
344
345 g_assert(qdict_size(dict1) == 2);
346 g_assert(qdict_size(dict2) == 0);
347
348 g_assert(qdict_get_int(dict1, "foo") == 42);
349 g_assert(qdict_get_int(dict1, "bar") == 23);
350
351 /* Test conflict */
352 qdict_put_int(dict2, "foo", 84);
353
354 qdict_join(dict1, dict2, overwrite);
355
356 g_assert(qdict_size(dict1) == 2);
357 g_assert(qdict_size(dict2) == !overwrite);
358
359 g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42));
360 g_assert(qdict_get_int(dict1, "bar") == 23);
361
362 if (!overwrite) {
363 g_assert(qdict_get_int(dict2, "foo") == 84);
364 }
365
366 /* Check the references */
367 g_assert(qdict_get(dict1, "foo")->base.refcnt == 1);
368 g_assert(qdict_get(dict1, "bar")->base.refcnt == 1);
369
370 if (!overwrite) {
371 g_assert(qdict_get(dict2, "foo")->base.refcnt == 1);
372 }
373
374 /* Clean up */
375 qdict_del(dict1, "foo");
376 qdict_del(dict1, "bar");
377
378 if (!overwrite) {
379 qdict_del(dict2, "foo");
380 }
381 } while (overwrite ^= true);
382
383 qobject_unref(dict1);
384 qobject_unref(dict2);
385}
386
387static void qdict_crumple_test_recursive(void)
388{
389 QDict *src, *dst, *rule, *vnc, *acl, *listen;
390 QList *rules;
391
392 src = qdict_new();
393 qdict_put_str(src, "vnc.listen.addr", "127.0.0.1");
394 qdict_put_str(src, "vnc.listen.port", "5901");
395 qdict_put_str(src, "vnc.acl.rules.0.match", "fred");
396 qdict_put_str(src, "vnc.acl.rules.0.policy", "allow");
397 qdict_put_str(src, "vnc.acl.rules.1.match", "bob");
398 qdict_put_str(src, "vnc.acl.rules.1.policy", "deny");
399 qdict_put_str(src, "vnc.acl.default", "deny");
400 qdict_put_str(src, "vnc.acl..name", "acl0");
401 qdict_put_str(src, "vnc.acl.rule..name", "acl0");
402
403 dst = qobject_to(QDict, qdict_crumple(src, &error_abort));
404 g_assert(dst);
405 g_assert_cmpint(qdict_size(dst), ==, 1);
406
407 vnc = qdict_get_qdict(dst, "vnc");
408 g_assert(vnc);
409 g_assert_cmpint(qdict_size(vnc), ==, 3);
410
411 listen = qdict_get_qdict(vnc, "listen");
412 g_assert(listen);
413 g_assert_cmpint(qdict_size(listen), ==, 2);
414 g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr"));
415 g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port"));
416
417 acl = qdict_get_qdict(vnc, "acl");
418 g_assert(acl);
419 g_assert_cmpint(qdict_size(acl), ==, 3);
420
421 rules = qdict_get_qlist(acl, "rules");
422 g_assert(rules);
423 g_assert_cmpint(qlist_size(rules), ==, 2);
424
425 rule = qobject_to(QDict, qlist_pop(rules));
426 g_assert(rule);
427 g_assert_cmpint(qdict_size(rule), ==, 2);
428 g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match"));
429 g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy"));
430 qobject_unref(rule);
431
432 rule = qobject_to(QDict, qlist_pop(rules));
433 g_assert(rule);
434 g_assert_cmpint(qdict_size(rule), ==, 2);
435 g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match"));
436 g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy"));
437 qobject_unref(rule);
438
439 /* With recursive crumpling, we should see all names unescaped */
440 g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name"));
441 g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name"));
442
443 qobject_unref(src);
444 qobject_unref(dst);
445}
446
447static void qdict_crumple_test_empty(void)
448{
449 QDict *src, *dst;
450
451 src = qdict_new();
452
453 dst = qobject_to(QDict, qdict_crumple(src, &error_abort));
454
455 g_assert_cmpint(qdict_size(dst), ==, 0);
456
457 qobject_unref(src);
458 qobject_unref(dst);
459}
460
461static int qdict_count_entries(QDict *dict)
462{
463 const QDictEntry *e;
464 int count = 0;
465
466 for (e = qdict_first(dict); e; e = qdict_next(dict, e)) {
467 count++;
468 }
469
470 return count;
471}
472
473static void qdict_rename_keys_test(void)
474{
475 QDict *dict = qdict_new();
476 QDict *copy;
477 QDictRenames *renames;
478 Error *local_err = NULL;
479
480 qdict_put_str(dict, "abc", "foo");
481 qdict_put_str(dict, "abcdef", "bar");
482 qdict_put_int(dict, "number", 42);
483 qdict_put_bool(dict, "flag", true);
484 qdict_put_null(dict, "nothing");
485
486 /* Empty rename list */
487 renames = (QDictRenames[]) {
488 { NULL, "this can be anything" }
489 };
490 copy = qdict_clone_shallow(dict);
491 qdict_rename_keys(copy, renames, &error_abort);
492
493 g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
494 g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
495 g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
496 g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
497 g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
498 g_assert_cmpint(qdict_count_entries(copy), ==, 5);
499
500 qobject_unref(copy);
501
502 /* Simple rename of all entries */
503 renames = (QDictRenames[]) {
504 { "abc", "str1" },
505 { "abcdef", "str2" },
506 { "number", "int" },
507 { "flag", "bool" },
508 { "nothing", "null" },
509 { NULL , NULL }
510 };
511 copy = qdict_clone_shallow(dict);
512 qdict_rename_keys(copy, renames, &error_abort);
513
514 g_assert(!qdict_haskey(copy, "abc"));
515 g_assert(!qdict_haskey(copy, "abcdef"));
516 g_assert(!qdict_haskey(copy, "number"));
517 g_assert(!qdict_haskey(copy, "flag"));
518 g_assert(!qdict_haskey(copy, "nothing"));
519
520 g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo");
521 g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar");
522 g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42);
523 g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true);
524 g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL);
525 g_assert_cmpint(qdict_count_entries(copy), ==, 5);
526
527 qobject_unref(copy);
528
529 /* Renames are processed top to bottom */
530 renames = (QDictRenames[]) {
531 { "abc", "tmp" },
532 { "abcdef", "abc" },
533 { "number", "abcdef" },
534 { "flag", "number" },
535 { "nothing", "flag" },
536 { "tmp", "nothing" },
537 { NULL , NULL }
538 };
539 copy = qdict_clone_shallow(dict);
540 qdict_rename_keys(copy, renames, &error_abort);
541
542 g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo");
543 g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar");
544 g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42);
545 g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true);
546 g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL);
547 g_assert(!qdict_haskey(copy, "tmp"));
548 g_assert_cmpint(qdict_count_entries(copy), ==, 5);
549
550 qobject_unref(copy);
551
552 /* Conflicting rename */
553 renames = (QDictRenames[]) {
554 { "abcdef", "abc" },
555 { NULL , NULL }
556 };
557 copy = qdict_clone_shallow(dict);
558 qdict_rename_keys(copy, renames, &local_err);
559
560 g_assert(local_err != NULL);
561 error_free(local_err);
562 local_err = NULL;
563
564 g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo");
565 g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar");
566 g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42);
567 g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true);
568 g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL);
569 g_assert_cmpint(qdict_count_entries(copy), ==, 5);
570
571 qobject_unref(copy);
572
573 /* Renames in an empty dict */
574 renames = (QDictRenames[]) {
575 { "abcdef", "abc" },
576 { NULL , NULL }
577 };
578
579 qobject_unref(dict);
580 dict = qdict_new();
581
582 qdict_rename_keys(dict, renames, &error_abort);
583 g_assert(qdict_first(dict) == NULL);
584
585 qobject_unref(dict);
586}
587
588static void qdict_crumple_test_bad_inputs(void)
589{
590 QDict *src;
591 Error *error = NULL;
592
593 src = qdict_new();
594 /* rule.0 can't be both a string and a dict */
595 qdict_put_str(src, "rule.0", "fred");
596 qdict_put_str(src, "rule.0.policy", "allow");
597
598 g_assert(qdict_crumple(src, &error) == NULL);
599 g_assert(error != NULL);
600 error_free(error);
601 error = NULL;
602 qobject_unref(src);
603
604 src = qdict_new();
605 /* rule can't be both a list and a dict */
606 qdict_put_str(src, "rule.0", "fred");
607 qdict_put_str(src, "rule.a", "allow");
608
609 g_assert(qdict_crumple(src, &error) == NULL);
610 g_assert(error != NULL);
611 error_free(error);
612 error = NULL;
613 qobject_unref(src);
614
615 src = qdict_new();
616 /* The input should be flat, ie no dicts or lists */
617 qdict_put(src, "rule.a", qdict_new());
618 qdict_put_str(src, "rule.b", "allow");
619
620 g_assert(qdict_crumple(src, &error) == NULL);
621 g_assert(error != NULL);
622 error_free(error);
623 error = NULL;
624 qobject_unref(src);
625
626 src = qdict_new();
627 /* List indexes must not have gaps */
628 qdict_put_str(src, "rule.0", "deny");
629 qdict_put_str(src, "rule.3", "allow");
630
631 g_assert(qdict_crumple(src, &error) == NULL);
632 g_assert(error != NULL);
633 error_free(error);
634 error = NULL;
635 qobject_unref(src);
636
637 src = qdict_new();
638 /* List indexes must be in %zu format */
639 qdict_put_str(src, "rule.0", "deny");
640 qdict_put_str(src, "rule.+1", "allow");
641
642 g_assert(qdict_crumple(src, &error) == NULL);
643 g_assert(error != NULL);
644 error_free(error);
645 error = NULL;
646 qobject_unref(src);
647}
648
649int main(int argc, char **argv)
650{
651 g_test_init(&argc, &argv, NULL);
652
653 g_test_add_func("/public/defaults", qdict_defaults_test);
654 g_test_add_func("/public/flatten", qdict_flatten_test);
655 g_test_add_func("/public/array_split", qdict_array_split_test);
656 g_test_add_func("/public/array_entries", qdict_array_entries_test);
657 g_test_add_func("/public/join", qdict_join_test);
658 g_test_add_func("/public/crumple/recursive",
659 qdict_crumple_test_recursive);
660 g_test_add_func("/public/crumple/empty",
661 qdict_crumple_test_empty);
662 g_test_add_func("/public/crumple/bad_inputs",
663 qdict_crumple_test_bad_inputs);
664
665 g_test_add_func("/public/rename_keys", qdict_rename_keys_test);
666
667 return g_test_run();
668}