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