test: fix missing va_end in case of test failures
[platform/upstream/libxkbcommon.git] / test / registry.c
1 /*
2  * Copyright © 2020 Red Hat, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #if HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #include "xkbcommon/xkbregistry.h"
36
37 #include "utils.h"
38
39 #define NO_VARIANT NULL
40
41 enum {
42     MODEL = 78,
43     LAYOUT,
44     VARIANT,
45     OPTION,
46 };
47
48 struct test_model {
49     const char *name; /* required */
50     const char *vendor;
51     const char *description;
52 };
53
54 struct test_layout {
55     const char *name; /* required */
56     const char *variant;
57     const char *brief;
58     const char *description;
59 };
60
61 struct test_option {
62     const char *name;
63     const char *description;
64 };
65
66 struct test_option_group {
67     const char *name;
68     const char *description;
69     bool allow_multiple_selection;
70
71     struct test_option options[10];
72 };
73
74 static void
75 fprint_config_item(FILE *fp,
76                    const char *name,
77                    const char *vendor,
78                    const char *brief,
79                    const char *description)
80 {
81     fprintf(fp, "  <configItem>\n"
82                 "    <name>%s</name>\n", name);
83     if (brief)
84         fprintf(fp, "    <shortDescription>%s</shortDescription>\n", brief);
85     if (description)
86         fprintf(fp, "    <description>%s</description>\n", description);
87     if (vendor)
88         fprintf(fp, "    <vendor>%s</vendor>\n", vendor);
89     fprintf(fp, "  </configItem>\n");
90 }
91
92 /**
93  * Create a directory populated with a rules/<ruleset>.xml that contains the
94  * given items.
95  *
96  * @return the XKB base directory
97  */
98 static char *
99 test_create_rules(const char *ruleset,
100                   const struct test_model *test_models,
101                   const struct test_layout *test_layouts,
102                   const struct test_option_group *test_groups)
103 {
104     static int iteration;
105     char *tmpdir;
106     char buf[PATH_MAX];
107     int rc;
108     FILE *fp;
109
110     tmpdir = asprintf_safe("/tmp/%s.%d.XXXXXX", ruleset, iteration++);
111     assert(tmpdir);
112     assert(mkdtemp(tmpdir) == tmpdir);
113
114     rc = snprintf_safe(buf, sizeof(buf), "%s/rules", tmpdir);
115     assert(rc);
116     rc = mkdir(buf, 0777);
117     assert(rc == 0);
118     rc = snprintf_safe(buf, sizeof(buf), "%s/rules/%s.xml", tmpdir, ruleset);
119     assert(rc);
120
121     fp = fopen(buf, "w");
122     assert(fp);
123
124     fprintf(fp,
125             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
126             "<!DOCTYPE xkbConfigRegistry SYSTEM \"xkb.dtd\">\n"
127             "<xkbConfigRegistry version=\"1.1\">\n");
128
129     if (test_models) {
130         fprintf(fp, "<modelList>\n");
131
132         for (const struct test_model *m = test_models; m->name; m++) {
133             fprintf(fp, "<model>\n");
134             fprint_config_item(fp, m->name, m->vendor, NULL, m->description);
135             fprintf(fp, "</model>\n");
136         }
137         fprintf(fp, "</modelList>\n");
138     }
139
140     if (test_layouts) {
141         const struct test_layout *l, *next;
142
143         fprintf(fp, "<layoutList>\n");
144
145         l = test_layouts;
146         next = l + 1;
147
148         assert(l->variant == NULL);
149
150         while (l->name) {
151             fprintf(fp, "<layout>\n");
152             fprint_config_item(fp, l->name, NULL, l->brief, l->description);
153
154             if (next->name && streq(next->name, l->name)) {
155                 fprintf(fp, "<variantList>\n");
156                 do {
157                     fprintf(fp, "<variant>\n");
158                     fprint_config_item(fp, next->variant, NULL, next->brief,
159                                        next->description);
160                     fprintf(fp, "</variant>\n");
161                     l = next;
162                     next++;
163                 } while (next->name && streq(next->name, l->name));
164                 fprintf(fp, "</variantList>\n");
165             }
166             fprintf(fp, "</layout>\n");
167             l++;
168         }
169         fprintf(fp, "</layoutList>\n");
170     }
171
172     if (test_groups) {
173         fprintf(fp, "<optionList>\n");
174
175         for (const struct test_option_group *g = test_groups; g->name; g++) {
176             fprintf(fp, "<group allowMultipleSelection=\"%s\">\n",
177                     g->allow_multiple_selection ? "true" : "false");
178             fprint_config_item(fp, g->name, NULL, NULL, g->description);
179             for (const struct test_option *o = g->options; o->name; o++) {
180                 fprintf(fp, "  <option>\n");
181                 fprint_config_item(fp, o->name, NULL, NULL, o->description);
182                 fprintf(fp, "</option>\n");
183             }
184             fprintf(fp, "</group>\n");
185         }
186         fprintf(fp, "</optionList>\n");
187     }
188
189     fprintf(fp, "</xkbConfigRegistry>\n");
190     fclose(fp);
191
192     return tmpdir;
193 }
194
195 static void
196 test_remove_rules(char *basedir, const char *ruleset)
197 {
198     char path[PATH_MAX];
199     int rc;
200
201     rc = snprintf_safe(path, sizeof(path), "%s/rules/%s.xml", basedir,
202                        ruleset);
203     assert(rc);
204     unlink(path);
205     rc = snprintf_safe(path, sizeof(path), "%s/xkb/rules", basedir);
206     assert(rc);
207     rmdir(path);
208     rmdir(basedir);
209     free(basedir);
210 }
211
212 static struct rxkb_context *
213 test_setup_context_for(const char *ruleset,
214                        struct test_model *system_models,
215                        struct test_model *user_models,
216                        struct test_layout *system_layouts,
217                        struct test_layout *user_layouts,
218                        struct test_option_group *system_groups,
219                        struct test_option_group *user_groups)
220 {
221     char *sysdir = NULL, *userdir = NULL;
222     struct rxkb_context *ctx;
223
224     sysdir = test_create_rules(ruleset, system_models, system_layouts,
225                                system_groups);
226     if (user_models || user_layouts || user_groups)
227         userdir = test_create_rules(ruleset, user_models, user_layouts,
228                                     user_groups);
229
230     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
231     assert(ctx);
232     if (userdir)
233         assert(rxkb_context_include_path_append(ctx, userdir));
234     assert(rxkb_context_include_path_append(ctx, sysdir));
235     assert(rxkb_context_parse(ctx, ruleset));
236
237     test_remove_rules(sysdir, ruleset);
238     if (userdir)
239         test_remove_rules(userdir, ruleset);
240
241     return ctx;
242 }
243
244 static struct rxkb_context *
245 test_setup_context(struct test_model *system_models,
246                    struct test_model *user_models,
247                    struct test_layout *system_layouts,
248                    struct test_layout *user_layouts,
249                    struct test_option_group *system_groups,
250                    struct test_option_group *user_groups)
251 {
252     const char *ruleset = "xkbtests";
253     return test_setup_context_for(ruleset, system_models,
254                                   user_models, system_layouts,
255                                   user_layouts, system_groups,
256                                   user_groups);
257 }
258
259 static struct rxkb_model *
260 fetch_model(struct rxkb_context *ctx, const char *model)
261 {
262     struct rxkb_model *m = rxkb_model_first(ctx);
263     while (m) {
264         if (streq(rxkb_model_get_name(m), model))
265             return rxkb_model_ref(m);
266         m = rxkb_model_next(m);
267     }
268     return NULL;
269 }
270
271 static bool
272 find_model(struct rxkb_context *ctx, const char *model)
273 {
274     struct rxkb_model *m = fetch_model(ctx, model);
275     rxkb_model_unref(m);
276     return m != NULL;
277 }
278
279 static bool
280 find_models(struct rxkb_context *ctx, ...)
281 {
282     va_list args;
283     const char *name;
284     int idx = 0;
285     bool rc = false;
286
287     va_start(args, ctx);
288     name = va_arg(args, const char *);
289     while(name) {
290         assert(++idx < 20); /* safety guard */
291         if (!find_model(ctx, name))
292             goto out;
293         name = va_arg(args, const char *);
294     };
295
296     rc = true;
297 out:
298     va_end(args);
299     return rc;
300 }
301
302 static struct rxkb_layout *
303 fetch_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
304 {
305     struct rxkb_layout *l = rxkb_layout_first(ctx);
306     while (l) {
307         const char *v = rxkb_layout_get_variant(l);
308
309         if (streq(rxkb_layout_get_name(l), layout) &&
310             ((v == NULL && variant == NULL) ||
311              (v != NULL && variant != NULL && streq(v, variant))))
312             return rxkb_layout_ref(l);
313         l = rxkb_layout_next(l);
314     }
315     return NULL;
316 }
317
318 static bool
319 find_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
320 {
321     struct rxkb_layout *l = fetch_layout(ctx, layout, variant);
322     rxkb_layout_unref(l);
323     return l != NULL;
324 }
325
326 static bool
327 find_layouts(struct rxkb_context *ctx, ...)
328 {
329     va_list args;
330     const char *name, *variant;
331     int idx = 0;
332     bool rc = false;
333
334     va_start(args, ctx);
335     name = va_arg(args, const char *);
336     variant = va_arg(args, const char *);
337     while(name) {
338         assert(++idx < 20); /* safety guard */
339         if (!find_layout(ctx, name, variant))
340             goto out;
341         name = va_arg(args, const char *);
342         if (name)
343             variant = va_arg(args, const char *);
344     };
345
346     rc = true;
347 out:
348     va_end(args);
349     return rc;
350 }
351
352 static struct rxkb_option_group *
353 fetch_option_group(struct rxkb_context *ctx, const char *grp)
354 {
355     struct rxkb_option_group *g = rxkb_option_group_first(ctx);
356     while (g) {
357         if (streq(grp, rxkb_option_group_get_name(g)))
358             return rxkb_option_group_ref(g);
359         g = rxkb_option_group_next(g);
360     }
361     return NULL;
362 }
363
364 static inline bool
365 find_option_group(struct rxkb_context *ctx, const char *grp)
366 {
367     struct rxkb_option_group *g = fetch_option_group(ctx, grp);
368     rxkb_option_group_unref(g);
369     return g != NULL;
370 }
371
372 static struct rxkb_option *
373 fetch_option(struct rxkb_context *ctx, const char *grp, const char *opt)
374 {
375     struct rxkb_option_group *g = rxkb_option_group_first(ctx);
376     while (g) {
377         if (streq(grp, rxkb_option_group_get_name(g))) {
378             struct rxkb_option *o = rxkb_option_first(g);
379
380             while (o) {
381                 if (streq(opt, rxkb_option_get_name(o)))
382                     return rxkb_option_ref(o);
383                 o = rxkb_option_next(o);
384             }
385         }
386         g = rxkb_option_group_next(g);
387     }
388     return NULL;
389 }
390
391 static bool
392 find_option(struct rxkb_context *ctx, const char *grp, const char *opt)
393 {
394     struct rxkb_option *o = fetch_option(ctx, grp, opt);
395     rxkb_option_unref(o);
396     return o != NULL;
397 }
398
399 static bool
400 find_options(struct rxkb_context *ctx, ...)
401 {
402     va_list args;
403     const char *grp, *opt;
404     int idx = 0;
405     bool rc = false;
406
407     va_start(args, ctx);
408     grp = va_arg(args, const char *);
409     opt = va_arg(args, const char *);
410     while(grp) {
411         assert(++idx < 20); /* safety guard */
412         if (!find_option(ctx, grp, opt))
413             goto out;
414         grp = va_arg(args, const char *);
415         if (grp)
416             opt = va_arg(args, const char *);
417     };
418
419     rc = true;
420 out:
421     va_end(args);
422     return rc;
423 }
424
425 static bool
426 cmp_models(struct test_model *tm, struct rxkb_model *m)
427 {
428     if (!tm || !m)
429         return false;
430
431     if (!streq(tm->name, rxkb_model_get_name(m)))
432         return false;
433
434     if (!streq_null(tm->vendor, rxkb_model_get_vendor(m)))
435         return false;
436
437     if (!streq_null(tm->description, rxkb_model_get_description(m)))
438         return false;
439
440     return true;
441 }
442
443 static bool
444 cmp_layouts(struct test_layout *tl, struct rxkb_layout *l)
445 {
446     if (!tl || !l)
447         return false;
448
449     if (!streq(tl->name, rxkb_layout_get_name(l)))
450         return false;
451
452     if (!streq_null(tl->variant, rxkb_layout_get_variant(l)))
453         return false;
454
455     if (!streq_null(tl->brief, rxkb_layout_get_brief(l)))
456         return false;
457
458     if (!streq_null(tl->description, rxkb_layout_get_description(l)))
459         return false;
460
461     return true;
462 }
463
464 static bool
465 cmp_options(struct test_option *to, struct rxkb_option *o)
466 {
467     if (!to || !o)
468         return false;
469
470     if (!streq(to->name, rxkb_option_get_name(o)))
471         return false;
472
473     if (!streq_null(to->description, rxkb_option_get_description(o)))
474         return false;
475
476     return true;
477 }
478
479 enum cmp_type {
480     CMP_EXACT,
481     CMP_MATCHING_ONLY,
482 };
483
484 static bool
485 cmp_option_groups(struct test_option_group *tg, struct rxkb_option_group *g,
486                   enum cmp_type cmp)
487 {
488     struct rxkb_option *o;
489     struct test_option *to;
490
491     if (!tg || !g)
492         return false;
493
494     if (!streq(tg->name, rxkb_option_group_get_name(g)))
495         return false;
496
497     if (!streq_null(tg->description, rxkb_option_group_get_description(g)))
498         return false;
499
500     if (tg->allow_multiple_selection != rxkb_option_group_allows_multiple(g))
501         return false;
502
503     to = tg->options;
504     o = rxkb_option_first(g);
505
506     while (o && to->name) {
507         if (!cmp_options(to, o))
508             return false;
509         to++;
510         o = rxkb_option_next(o);
511     }
512
513     if (cmp == CMP_EXACT && (o || to->name))
514         return false;
515
516     return true;
517 }
518
519 static void
520 test_load_basic(void)
521 {
522     struct test_model system_models[] =  {
523         {"m1"},
524         {"m2"},
525         {NULL},
526     };
527     struct test_layout system_layouts[] =  {
528         {"l1"},
529         {"l1", "v1"},
530         {NULL},
531     };
532     struct test_option_group system_groups[] = {
533         {"grp1", NULL, true,
534           { {"grp1:1"}, {"grp1:2"} } },
535         {"grp2", NULL, false,
536           { {"grp2:1"}, {"grp2:2"} } },
537         { NULL },
538     };
539     struct rxkb_context *ctx;
540
541     ctx = test_setup_context(system_models, NULL,
542                              system_layouts, NULL,
543                              system_groups, NULL);
544
545     assert(find_models(ctx, "m1", "m2", NULL));
546     assert(find_layouts(ctx, "l1", NO_VARIANT,
547                              "l1", "v1", NULL));
548     assert(find_options(ctx, "grp1", "grp1:1",
549                              "grp1", "grp1:2",
550                              "grp2", "grp2:1",
551                              "grp2", "grp2:2", NULL));
552     rxkb_context_unref(ctx);
553 }
554
555 static void
556 test_load_full(void)
557 {
558     struct test_model system_models[] =  {
559         {"m1", "vendor1", "desc1"},
560         {"m2", "vendor2", "desc2"},
561         {NULL},
562     };
563     struct test_layout system_layouts[] =  {
564         {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
565         {"l1", "v1", "vbrief1", "vdesc1"},
566         {NULL},
567     };
568     struct test_option_group system_groups[] = {
569         {"grp1", "gdesc1", true,
570           { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
571         {"grp2", "gdesc2", false,
572           { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
573         { NULL },
574     };
575     struct rxkb_context *ctx;
576     struct rxkb_model *m;
577     struct rxkb_layout *l;
578     struct rxkb_option_group *g;
579
580     ctx = test_setup_context(system_models, NULL,
581                              system_layouts, NULL,
582                              system_groups, NULL);
583
584     m = fetch_model(ctx, "m1");
585     assert(cmp_models(&system_models[0], m));
586     rxkb_model_unref(m);
587
588     m = fetch_model(ctx, "m2");
589     assert(cmp_models(&system_models[1], m));
590     rxkb_model_unref(m);
591
592     l = fetch_layout(ctx, "l1", NO_VARIANT);
593     assert(cmp_layouts(&system_layouts[0], l));
594     rxkb_layout_unref(l);
595
596     l = fetch_layout(ctx, "l1", "v1");
597     assert(cmp_layouts(&system_layouts[1], l));
598     rxkb_layout_unref(l);
599
600     g = fetch_option_group(ctx, "grp1");
601     assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
602     rxkb_option_group_unref(g);
603
604     g = fetch_option_group(ctx, "grp2");
605     assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
606     rxkb_option_group_unref(g);
607
608     rxkb_context_unref(ctx);
609 }
610
611 static void
612 test_popularity(void)
613 {
614     struct test_layout system_layouts[] =  {
615         {"l1", NO_VARIANT },
616         {"l1", "v1" },
617         {NULL},
618     };
619     struct rxkb_context *ctx;
620     struct rxkb_layout *l;
621     const char *ruleset = "xkbtests.extras";
622     char *dir = NULL;
623
624     dir = test_create_rules(ruleset, NULL, system_layouts, NULL);
625     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES |
626                            RXKB_CONTEXT_LOAD_EXOTIC_RULES);
627     assert(ctx);
628     assert(rxkb_context_include_path_append(ctx, dir));
629     /* Hack: rulest above generates xkbtests.extras.xml, loading "xkbtests"
630      * means the extras file counts as exotic */
631     assert(rxkb_context_parse(ctx, "xkbtests"));
632
633     l = fetch_layout(ctx, "l1", NO_VARIANT);
634     assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
635     rxkb_layout_unref(l);
636
637     l = fetch_layout(ctx, "l1", "v1");
638     assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
639     rxkb_layout_unref(l);
640
641     test_remove_rules(dir, ruleset);
642     rxkb_context_unref(ctx);
643 }
644
645
646 static void
647 test_load_merge(void)
648 {
649     struct test_model system_models[] =  {
650         {"m1", "vendor1", "desc1"},
651         {"m2", "vendor2", "desc2"},
652         {NULL},
653     };
654     struct test_model user_models[] =  {
655         {"m3", "vendor3", "desc3"},
656         {"m4", "vendor4", "desc4"},
657         {NULL},
658     };
659     struct test_layout system_layouts[] =  {
660         {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
661         {"l1", "v1", "vbrief1", "vdesc1"},
662         {NULL},
663     };
664     struct test_layout user_layouts[] =  {
665         {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
666         {"l2", "v2", "vbrief2", "vdesc2"},
667         {NULL},
668     };
669     struct test_option_group system_groups[] = {
670         {"grp1", NULL, true,
671           { {"grp1:1"}, {"grp1:2"} } },
672         {"grp2", NULL, false,
673           { {"grp2:1"}, {"grp2:2"} } },
674         { NULL },
675     };
676     struct test_option_group user_groups[] = {
677         {"grp3", NULL, true,
678           { {"grp3:1"}, {"grp3:2"} } },
679         {"grp4", NULL, false,
680           { {"grp4:1"}, {"grp4:2"} } },
681         { NULL },
682     };
683     struct rxkb_context *ctx;
684     struct rxkb_model *m;
685     struct rxkb_layout *l;
686     struct rxkb_option_group *g;
687
688     ctx = test_setup_context(system_models, user_models,
689                              system_layouts, user_layouts,
690                              system_groups, user_groups);
691
692     assert(find_models(ctx, "m1", "m2", "m3", "m4", NULL));
693     assert(find_layouts(ctx, "l1", NO_VARIANT,
694                              "l1", "v1",
695                              "l2", NO_VARIANT,
696                              "l2", "v2", NULL));
697
698     m = fetch_model(ctx, "m1");
699     assert(cmp_models(&system_models[0], m));
700     rxkb_model_unref(m);
701
702     m = fetch_model(ctx, "m2");
703     assert(cmp_models(&system_models[1], m));
704     rxkb_model_unref(m);
705
706     m = fetch_model(ctx, "m3");
707     assert(cmp_models(&user_models[0], m));
708     rxkb_model_unref(m);
709
710     m = fetch_model(ctx, "m4");
711     assert(cmp_models(&user_models[1], m));
712     rxkb_model_unref(m);
713
714     l = fetch_layout(ctx, "l1", NO_VARIANT);
715     assert(cmp_layouts(&system_layouts[0], l));
716     rxkb_layout_unref(l);
717
718     l = fetch_layout(ctx, "l1", "v1");
719     assert(cmp_layouts(&system_layouts[1], l));
720     rxkb_layout_unref(l);
721
722     l = fetch_layout(ctx, "l2", NO_VARIANT);
723     assert(cmp_layouts(&user_layouts[0], l));
724     rxkb_layout_unref(l);
725
726     l = fetch_layout(ctx, "l2", "v2");
727     assert(cmp_layouts(&user_layouts[1], l));
728     rxkb_layout_unref(l);
729
730     g = fetch_option_group(ctx, "grp1");
731     assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
732     rxkb_option_group_unref(g);
733
734     g = fetch_option_group(ctx, "grp2");
735     assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
736     rxkb_option_group_unref(g);
737
738     g = fetch_option_group(ctx, "grp3");
739     assert(cmp_option_groups(&user_groups[0], g, CMP_EXACT));
740     rxkb_option_group_unref(g);
741
742     g = fetch_option_group(ctx, "grp4");
743     assert(cmp_option_groups(&user_groups[1], g, CMP_EXACT));
744     rxkb_option_group_unref(g);
745
746     rxkb_context_unref(ctx);
747 }
748
749 static void
750 test_load_merge_no_overwrite(void)
751 {
752     struct test_model system_models[] =  {
753         {"m1", "vendor1", "desc1"},
754         {"m2", "vendor2", "desc2"},
755         {NULL},
756     };
757     struct test_model user_models[] =  {
758         {"m1", "vendor3", "desc3"}, /* must not overwrite */
759         {"m4", "vendor4", "desc4"},
760         {NULL},
761     };
762     struct test_layout system_layouts[] =  {
763         {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
764         {"l1", "v1", "vbrief1", "vdesc1"},
765         {NULL},
766     };
767     struct test_layout user_layouts[] =  {
768         {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
769         {"l2", "v2", "vbrief2", "vdesc2"},
770         {"l1", NO_VARIANT, "lbrief3", "ldesc3"}, /* must not overwrite */
771         {"l1", "v2", "vbrief3", "vdesc3"}, /* must not overwrite */
772         {NULL},
773     };
774     struct test_option_group system_groups[] = {
775         {"grp1", "gdesc1", true,
776           { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
777         {"grp2", "gdesc2", false,
778           { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
779         { NULL },
780     };
781     struct test_option_group user_groups[] = {
782         {"grp1", "XXXXX", false, /* must not overwrite */
783           { {"grp1:1", "YYYYYYY"}, /* must not overwrite */
784             {"grp1:3", "ZZZZZZ"} } }, /* append */
785         {"grp4", "gdesc4", false,
786           { {"grp4:1", "odesc41"}, {"grp4:2", "odesc42"} } },
787         { NULL },
788     };
789     struct rxkb_context *ctx;
790     struct rxkb_model *m;
791     struct rxkb_layout *l;
792     struct rxkb_option_group *g;
793
794     ctx = test_setup_context(system_models, user_models,
795                              system_layouts, user_layouts,
796                              system_groups, user_groups);
797
798     m = fetch_model(ctx, "m1");
799     assert(cmp_models(&system_models[0], m));
800     rxkb_model_unref(m);
801
802     l = fetch_layout(ctx, "l1", NO_VARIANT);
803     assert(cmp_layouts(&system_layouts[0], l));
804     rxkb_layout_unref(l);
805
806     l = fetch_layout(ctx, "l1", "v1");
807     assert(cmp_layouts(&system_layouts[1], l));
808     rxkb_layout_unref(l);
809
810     assert(find_option(ctx, "grp1", "grp1:3"));
811     g = fetch_option_group(ctx, "grp1");
812     assert(cmp_option_groups(&system_groups[0], g, CMP_MATCHING_ONLY));
813     rxkb_option_group_unref(g);
814
815     rxkb_context_unref(ctx);
816 }
817
818 static void
819 test_no_include_paths(void)
820 {
821     struct rxkb_context *ctx;
822
823     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
824     assert(ctx);
825     assert(!rxkb_context_parse_default_ruleset(ctx));
826
827     rxkb_context_unref(ctx);
828 }
829
830 static void
831 test_invalid_include(void)
832 {
833     struct rxkb_context *ctx;
834
835     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
836     assert(ctx);
837     assert(!rxkb_context_include_path_append(ctx, "/foo/bar/baz/bat"));
838     assert(!rxkb_context_parse_default_ruleset(ctx));
839
840     rxkb_context_unref(ctx);
841 }
842
843 int
844 main(void)
845 {
846     test_no_include_paths();
847     test_invalid_include();
848     test_load_basic();
849     test_load_full();
850     test_load_merge();
851     test_load_merge_no_overwrite();
852     test_popularity();
853
854     return 0;
855 }