packaging: make a option to select enable/disable tools
[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 #include "test.h"
39
40 #define NO_VARIANT NULL
41
42 enum {
43     MODEL = 78,
44     LAYOUT,
45     VARIANT,
46     OPTION,
47 };
48
49 struct test_model {
50     const char *name; /* required */
51     const char *vendor;
52     const char *description;
53 };
54
55 struct test_layout {
56     const char *name; /* required */
57     const char *variant;
58     const char *brief;
59     const char *description;
60     const char *iso639[3];  /* language list (iso639 three letter codes), 3 is enough for our test  */
61     const char *iso3166[3]; /* country list (iso3166 two letter codes), 3 is enough for our tests */
62 };
63
64 struct test_option {
65     const char *name;
66     const char *description;
67 };
68
69 struct test_option_group {
70     const char *name;
71     const char *description;
72     bool allow_multiple_selection;
73
74     struct test_option options[10];
75 };
76
77 static void
78 fprint_config_item(FILE *fp,
79                    const char *name,
80                    const char *vendor,
81                    const char *brief,
82                    const char *description,
83                    const char * const iso639[3],
84                    const char * const iso3166[3])
85 {
86     fprintf(fp, "  <configItem>\n"
87                 "    <name>%s</name>\n", name);
88     if (brief)
89         fprintf(fp, "    <shortDescription>%s</shortDescription>\n", brief);
90     if (description)
91         fprintf(fp, "    <description>%s</description>\n", description);
92     if (vendor)
93         fprintf(fp, "    <vendor>%s</vendor>\n", vendor);
94     if (iso3166 && iso3166[0]) {
95         fprintf(fp, "    <countryList>\n");
96         for (int i = 0; i < 3; i++) {
97             const char *iso = iso3166[i];
98             if (!iso)
99                 break;
100             fprintf(fp, "        <iso3166Id>%s</iso3166Id>\n", iso);
101         }
102         fprintf(fp, "    </countryList>\n");
103     }
104     if (iso639 && iso639[0]) {
105         fprintf(fp, "    <languageList>\n");
106         for (int i = 0; i < 3; i++) {
107             const char *iso = iso639[i];
108             if (!iso)
109                 break;
110             fprintf(fp, "        <iso639Id>%s</iso639Id>\n", iso);
111         }
112         fprintf(fp, "    </languageList>\n");
113     }
114
115     fprintf(fp, "  </configItem>\n");
116 }
117
118 /**
119  * Create a directory populated with a rules/<ruleset>.xml that contains the
120  * given items.
121  *
122  * @return the XKB base directory
123  */
124 static char *
125 test_create_rules(const char *ruleset,
126                   const struct test_model *test_models,
127                   const struct test_layout *test_layouts,
128                   const struct test_option_group *test_groups)
129 {
130     static int iteration;
131     char *tmpdir;
132     char buf[PATH_MAX];
133     int rc;
134     FILE *fp;
135
136     char *template = asprintf_safe("%s.%d.XXXXXX", ruleset, iteration++);
137     assert(template != NULL);
138     tmpdir = test_maketempdir(template);
139     free(template);
140
141     free(test_makedir(tmpdir, "rules"));
142
143     rc = snprintf_safe(buf, sizeof(buf), "%s/rules/%s.xml", tmpdir, ruleset);
144     assert(rc);
145
146     fp = fopen(buf, "w");
147     assert(fp);
148
149     fprintf(fp,
150             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
151             "<!DOCTYPE xkbConfigRegistry SYSTEM \"xkb.dtd\">\n"
152             "<xkbConfigRegistry version=\"1.1\">\n");
153
154     if (test_models) {
155         fprintf(fp, "<modelList>\n");
156
157         for (const struct test_model *m = test_models; m->name; m++) {
158             fprintf(fp, "<model>\n");
159             fprint_config_item(fp, m->name, m->vendor, NULL, m->description, NULL, NULL);
160             fprintf(fp, "</model>\n");
161         }
162         fprintf(fp, "</modelList>\n");
163     }
164
165     if (test_layouts) {
166         const struct test_layout *l, *next;
167
168         fprintf(fp, "<layoutList>\n");
169
170         l = test_layouts;
171         next = l + 1;
172
173         assert(l->variant == NULL);
174
175         while (l->name) {
176             fprintf(fp, "<layout>\n");
177             fprint_config_item(fp, l->name, NULL, l->brief, l->description, l->iso639, l->iso3166);
178
179             if (next->name && streq(next->name, l->name)) {
180                 fprintf(fp, "<variantList>\n");
181                 do {
182                     fprintf(fp, "<variant>\n");
183                     fprint_config_item(fp, next->variant, NULL, next->brief, next->description, next->iso639, next->iso3166);
184                     fprintf(fp, "</variant>\n");
185                     l = next;
186                     next++;
187                 } while (next->name && streq(next->name, l->name));
188                 fprintf(fp, "</variantList>\n");
189             }
190             fprintf(fp, "</layout>\n");
191             l++;
192         }
193         fprintf(fp, "</layoutList>\n");
194     }
195
196     if (test_groups) {
197         fprintf(fp, "<optionList>\n");
198
199         for (const struct test_option_group *g = test_groups; g->name; g++) {
200             fprintf(fp, "<group allowMultipleSelection=\"%s\">\n",
201                     g->allow_multiple_selection ? "true" : "false");
202             fprint_config_item(fp, g->name, NULL, NULL, g->description, NULL, NULL);
203             for (const struct test_option *o = g->options; o->name; o++) {
204                 fprintf(fp, "  <option>\n");
205                 fprint_config_item(fp, o->name, NULL, NULL, o->description, NULL, NULL);
206                 fprintf(fp, "</option>\n");
207             }
208             fprintf(fp, "</group>\n");
209         }
210         fprintf(fp, "</optionList>\n");
211     }
212
213     fprintf(fp, "</xkbConfigRegistry>\n");
214     fclose(fp);
215
216     return tmpdir;
217 }
218
219 static void
220 test_remove_rules(char *basedir, const char *ruleset)
221 {
222     char path[PATH_MAX];
223     int rc;
224
225     rc = snprintf_safe(path, sizeof(path), "%s/rules/%s.xml", basedir,
226                        ruleset);
227     assert(rc);
228     unlink(path);
229     rc = snprintf_safe(path, sizeof(path), "%s/xkb/rules", basedir);
230     assert(rc);
231     rmdir(path);
232     rmdir(basedir);
233     free(basedir);
234 }
235
236 static struct rxkb_context *
237 test_setup_context_for(const char *ruleset,
238                        struct test_model *system_models,
239                        struct test_model *user_models,
240                        struct test_layout *system_layouts,
241                        struct test_layout *user_layouts,
242                        struct test_option_group *system_groups,
243                        struct test_option_group *user_groups)
244 {
245     char *sysdir = NULL, *userdir = NULL;
246     struct rxkb_context *ctx;
247
248     sysdir = test_create_rules(ruleset, system_models, system_layouts,
249                                system_groups);
250     if (user_models || user_layouts || user_groups)
251         userdir = test_create_rules(ruleset, user_models, user_layouts,
252                                     user_groups);
253
254     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
255     assert(ctx);
256     if (userdir)
257         assert(rxkb_context_include_path_append(ctx, userdir));
258     assert(rxkb_context_include_path_append(ctx, sysdir));
259     assert(rxkb_context_parse(ctx, ruleset));
260
261     test_remove_rules(sysdir, ruleset);
262     if (userdir)
263         test_remove_rules(userdir, ruleset);
264
265     return ctx;
266 }
267
268 static struct rxkb_context *
269 test_setup_context(struct test_model *system_models,
270                    struct test_model *user_models,
271                    struct test_layout *system_layouts,
272                    struct test_layout *user_layouts,
273                    struct test_option_group *system_groups,
274                    struct test_option_group *user_groups)
275 {
276     const char *ruleset = "xkbtests";
277     return test_setup_context_for(ruleset, system_models,
278                                   user_models, system_layouts,
279                                   user_layouts, system_groups,
280                                   user_groups);
281 }
282
283 static struct rxkb_model *
284 fetch_model(struct rxkb_context *ctx, const char *model)
285 {
286     struct rxkb_model *m = rxkb_model_first(ctx);
287     while (m) {
288         if (streq(rxkb_model_get_name(m), model))
289             return rxkb_model_ref(m);
290         m = rxkb_model_next(m);
291     }
292     return NULL;
293 }
294
295 static bool
296 find_model(struct rxkb_context *ctx, const char *model)
297 {
298     struct rxkb_model *m = fetch_model(ctx, model);
299     rxkb_model_unref(m);
300     return m != NULL;
301 }
302
303 static bool
304 find_models(struct rxkb_context *ctx, ...)
305 {
306     va_list args;
307     const char *name;
308     int idx = 0;
309     bool rc = false;
310
311     va_start(args, ctx);
312     name = va_arg(args, const char *);
313     while(name) {
314         assert(++idx < 20); /* safety guard */
315         if (!find_model(ctx, name))
316             goto out;
317         name = va_arg(args, const char *);
318     };
319
320     rc = true;
321 out:
322     va_end(args);
323     return rc;
324 }
325
326 static struct rxkb_layout *
327 fetch_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
328 {
329     struct rxkb_layout *l = rxkb_layout_first(ctx);
330     while (l) {
331         const char *v = rxkb_layout_get_variant(l);
332
333         if (streq(rxkb_layout_get_name(l), layout) &&
334             ((v == NULL && variant == NULL) ||
335              (v != NULL && variant != NULL && streq(v, variant))))
336             return rxkb_layout_ref(l);
337         l = rxkb_layout_next(l);
338     }
339     return NULL;
340 }
341
342 static bool
343 find_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
344 {
345     struct rxkb_layout *l = fetch_layout(ctx, layout, variant);
346     rxkb_layout_unref(l);
347     return l != NULL;
348 }
349
350 static bool
351 find_layouts(struct rxkb_context *ctx, ...)
352 {
353     va_list args;
354     const char *name, *variant;
355     int idx = 0;
356     bool rc = false;
357
358     va_start(args, ctx);
359     name = va_arg(args, const char *);
360     variant = va_arg(args, const char *);
361     while(name) {
362         assert(++idx < 20); /* safety guard */
363         if (!find_layout(ctx, name, variant))
364             goto out;
365         name = va_arg(args, const char *);
366         if (name)
367             variant = va_arg(args, const char *);
368     };
369
370     rc = true;
371 out:
372     va_end(args);
373     return rc;
374 }
375
376 static struct rxkb_option_group *
377 fetch_option_group(struct rxkb_context *ctx, const char *grp)
378 {
379     struct rxkb_option_group *g = rxkb_option_group_first(ctx);
380     while (g) {
381         if (streq(grp, rxkb_option_group_get_name(g)))
382             return rxkb_option_group_ref(g);
383         g = rxkb_option_group_next(g);
384     }
385     return NULL;
386 }
387
388 static inline bool
389 find_option_group(struct rxkb_context *ctx, const char *grp)
390 {
391     struct rxkb_option_group *g = fetch_option_group(ctx, grp);
392     rxkb_option_group_unref(g);
393     return g != NULL;
394 }
395
396 static struct rxkb_option *
397 fetch_option(struct rxkb_context *ctx, const char *grp, const char *opt)
398 {
399     struct rxkb_option_group *g = rxkb_option_group_first(ctx);
400     while (g) {
401         if (streq(grp, rxkb_option_group_get_name(g))) {
402             struct rxkb_option *o = rxkb_option_first(g);
403
404             while (o) {
405                 if (streq(opt, rxkb_option_get_name(o)))
406                     return rxkb_option_ref(o);
407                 o = rxkb_option_next(o);
408             }
409         }
410         g = rxkb_option_group_next(g);
411     }
412     return NULL;
413 }
414
415 static bool
416 find_option(struct rxkb_context *ctx, const char *grp, const char *opt)
417 {
418     struct rxkb_option *o = fetch_option(ctx, grp, opt);
419     rxkb_option_unref(o);
420     return o != NULL;
421 }
422
423 static bool
424 find_options(struct rxkb_context *ctx, ...)
425 {
426     va_list args;
427     const char *grp, *opt;
428     int idx = 0;
429     bool rc = false;
430
431     va_start(args, ctx);
432     grp = va_arg(args, const char *);
433     opt = va_arg(args, const char *);
434     while(grp) {
435         assert(++idx < 20); /* safety guard */
436         if (!find_option(ctx, grp, opt))
437             goto out;
438         grp = va_arg(args, const char *);
439         if (grp)
440             opt = va_arg(args, const char *);
441     };
442
443     rc = true;
444 out:
445     va_end(args);
446     return rc;
447 }
448
449 static bool
450 cmp_models(struct test_model *tm, struct rxkb_model *m)
451 {
452     if (!tm || !m)
453         return false;
454
455     if (!streq(tm->name, rxkb_model_get_name(m)))
456         return false;
457
458     if (!streq_null(tm->vendor, rxkb_model_get_vendor(m)))
459         return false;
460
461     if (!streq_null(tm->description, rxkb_model_get_description(m)))
462         return false;
463
464     return true;
465 }
466
467 static bool
468 cmp_layouts(struct test_layout *tl, struct rxkb_layout *l)
469 {
470     struct rxkb_iso3166_code *iso3166 = NULL;
471     struct rxkb_iso639_code *iso639 = NULL;
472
473     if (!tl || !l)
474         return false;
475
476     if (!streq(tl->name, rxkb_layout_get_name(l)))
477         return false;
478
479     if (!streq_null(tl->variant, rxkb_layout_get_variant(l)))
480         return false;
481
482     if (!streq_null(tl->brief, rxkb_layout_get_brief(l)))
483         return false;
484
485     if (!streq_null(tl->description, rxkb_layout_get_description(l)))
486         return false;
487
488     iso3166 = rxkb_layout_get_iso3166_first(l);
489     for (size_t i = 0; i < sizeof(tl->iso3166); i++) {
490         const char *iso = tl->iso3166[i];
491         if (iso == NULL && iso3166 == NULL)
492             break;
493
494         if (!streq_null(iso, rxkb_iso3166_code_get_code(iso3166)))
495             return false;
496
497         iso3166 = rxkb_iso3166_code_next(iso3166);
498     }
499
500     if (iso3166 != NULL)
501         return false;
502
503     iso639 = rxkb_layout_get_iso639_first(l);
504     for (size_t i = 0; i < sizeof(tl->iso639); i++) {
505         const char *iso = tl->iso639[i];
506         if (iso == NULL && iso639 == NULL)
507             break;
508
509         if (!streq_null(iso, rxkb_iso639_code_get_code(iso639)))
510             return false;
511
512         iso639 = rxkb_iso639_code_next(iso639);
513     }
514
515     if (iso639 != NULL)
516         return false;
517
518     return true;
519 }
520
521 static bool
522 cmp_options(struct test_option *to, struct rxkb_option *o)
523 {
524     if (!to || !o)
525         return false;
526
527     if (!streq(to->name, rxkb_option_get_name(o)))
528         return false;
529
530     if (!streq_null(to->description, rxkb_option_get_description(o)))
531         return false;
532
533     return true;
534 }
535
536 enum cmp_type {
537     CMP_EXACT,
538     CMP_MATCHING_ONLY,
539 };
540
541 static bool
542 cmp_option_groups(struct test_option_group *tg, struct rxkb_option_group *g,
543                   enum cmp_type cmp)
544 {
545     struct rxkb_option *o;
546     struct test_option *to;
547
548     if (!tg || !g)
549         return false;
550
551     if (!streq(tg->name, rxkb_option_group_get_name(g)))
552         return false;
553
554     if (!streq_null(tg->description, rxkb_option_group_get_description(g)))
555         return false;
556
557     if (tg->allow_multiple_selection != rxkb_option_group_allows_multiple(g))
558         return false;
559
560     to = tg->options;
561     o = rxkb_option_first(g);
562
563     while (o && to->name) {
564         if (!cmp_options(to, o))
565             return false;
566         to++;
567         o = rxkb_option_next(o);
568     }
569
570     if (cmp == CMP_EXACT && (o || to->name))
571         return false;
572
573     return true;
574 }
575
576 static void
577 test_load_basic(void)
578 {
579     struct test_model system_models[] =  {
580         {"m1"},
581         {"m2"},
582         {NULL},
583     };
584     struct test_layout system_layouts[] =  {
585         {"l1"},
586         {"l1", "v1"},
587         {NULL},
588     };
589     struct test_option_group system_groups[] = {
590         {"grp1", NULL, true,
591           { {"grp1:1"}, {"grp1:2"} } },
592         {"grp2", NULL, false,
593           { {"grp2:1"}, {"grp2:2"} } },
594         { NULL },
595     };
596     struct rxkb_context *ctx;
597
598     ctx = test_setup_context(system_models, NULL,
599                              system_layouts, NULL,
600                              system_groups, NULL);
601
602     assert(find_models(ctx, "m1", "m2", NULL));
603     assert(find_layouts(ctx, "l1", NO_VARIANT,
604                              "l1", "v1", NULL));
605     assert(find_options(ctx, "grp1", "grp1:1",
606                              "grp1", "grp1:2",
607                              "grp2", "grp2:1",
608                              "grp2", "grp2:2", NULL));
609     rxkb_context_unref(ctx);
610 }
611
612 static void
613 test_load_full(void)
614 {
615     struct test_model system_models[] =  {
616         {"m1", "vendor1", "desc1"},
617         {"m2", "vendor2", "desc2"},
618         {NULL},
619     };
620     struct test_layout system_layouts[] =  {
621         {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
622         {"l1", "v1", "vbrief1", "vdesc1"},
623         {"l1", "v2", NULL, "vdesc2"},
624         {NULL},
625     };
626     struct test_option_group system_groups[] = {
627         {"grp1", "gdesc1", true,
628           { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
629         {"grp2", "gdesc2", false,
630           { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
631         { NULL },
632     };
633     struct rxkb_context *ctx;
634     struct rxkb_model *m;
635     struct rxkb_layout *l;
636     struct rxkb_option_group *g;
637
638     ctx = test_setup_context(system_models, NULL,
639                              system_layouts, NULL,
640                              system_groups, NULL);
641
642     m = fetch_model(ctx, "m1");
643     assert(cmp_models(&system_models[0], m));
644     rxkb_model_unref(m);
645
646     m = fetch_model(ctx, "m2");
647     assert(cmp_models(&system_models[1], m));
648     rxkb_model_unref(m);
649
650     l = fetch_layout(ctx, "l1", NO_VARIANT);
651     assert(cmp_layouts(&system_layouts[0], l));
652     rxkb_layout_unref(l);
653
654     l = fetch_layout(ctx, "l1", "v1");
655     assert(cmp_layouts(&system_layouts[1], l));
656     rxkb_layout_unref(l);
657
658     l = fetch_layout(ctx, "l1", "v2");
659     struct test_layout expected = {"l1", "v2", "lbrief1", "vdesc2"};
660     assert(cmp_layouts(&expected, l));
661     rxkb_layout_unref(l);
662
663     g = fetch_option_group(ctx, "grp1");
664     assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
665     rxkb_option_group_unref(g);
666
667     g = fetch_option_group(ctx, "grp2");
668     assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
669     rxkb_option_group_unref(g);
670
671     rxkb_context_unref(ctx);
672 }
673
674 static void
675 test_load_languages(void)
676 {
677     struct test_model system_models[] =  {
678         {"m1", "vendor1", "desc1"},
679         {NULL},
680     };
681     struct test_layout system_layouts[] =  {
682         {"l1", NO_VARIANT, "lbrief1", "ldesc1",
683             .iso639 = { "abc", "def" },
684             .iso3166 = { "uv", "wx" }},
685         {"l1", "v1", "vbrief1", "vdesc1",
686             .iso639 = {"efg"},
687             .iso3166 = {"yz"}},
688         {"l2", NO_VARIANT, "lbrief1", "ldesc1",
689             .iso639 = { "hij", "klm" },
690             .iso3166 = { "op", "qr" }},
691         {"l2", "v2", "lbrief1", "ldesc1",
692             .iso639 = { NULL }, /* inherit from parent */
693             .iso3166 = { NULL }},  /* inherit from parent */
694         {NULL},
695     };
696     struct test_option_group system_groups[] = {
697         {"grp1", "gdesc1", true,
698           { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
699         { NULL },
700     };
701     struct rxkb_context *ctx;
702     struct rxkb_layout *l;
703     struct rxkb_iso3166_code *iso3166;
704     struct rxkb_iso639_code *iso639;
705
706     ctx = test_setup_context(system_models, NULL,
707                              system_layouts, NULL,
708                              system_groups, NULL);
709
710     l = fetch_layout(ctx, "l1", NO_VARIANT);
711     assert(cmp_layouts(&system_layouts[0], l));
712     rxkb_layout_unref(l);
713
714     l = fetch_layout(ctx, "l1", "v1");
715     assert(cmp_layouts(&system_layouts[1], l));
716     rxkb_layout_unref(l);
717
718     l = fetch_layout(ctx, "l2", "v2");
719     iso3166 = rxkb_layout_get_iso3166_first(l);
720     assert(streq(rxkb_iso3166_code_get_code(iso3166), "op"));
721     iso3166 = rxkb_iso3166_code_next(iso3166);
722     assert(streq(rxkb_iso3166_code_get_code(iso3166), "qr"));
723
724     iso639 = rxkb_layout_get_iso639_first(l);
725     assert(streq(rxkb_iso639_code_get_code(iso639), "hij"));
726     iso639 = rxkb_iso639_code_next(iso639);
727     assert(streq(rxkb_iso639_code_get_code(iso639), "klm"));
728
729     rxkb_layout_unref(l);
730     rxkb_context_unref(ctx);
731 }
732
733 static void
734 test_load_invalid_languages(void)
735 {
736     struct test_model system_models[] =  {
737         {"m1", "vendor1", "desc1"},
738         {NULL},
739     };
740     struct test_layout system_layouts[] =  {
741         {"l1", NO_VARIANT, "lbrief1", "ldesc1",
742             .iso639 = { "ab", "def" },
743             .iso3166 = { "uvw", "xz" }},
744         {NULL},
745     };
746     struct test_option_group system_groups[] = {
747         {"grp1", "gdesc1", true,
748           { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
749         { NULL },
750     };
751     struct rxkb_context *ctx;
752     struct rxkb_layout *l;
753     struct rxkb_iso3166_code *iso3166;
754     struct rxkb_iso639_code *iso639;
755
756     ctx = test_setup_context(system_models, NULL,
757                              system_layouts, NULL,
758                              system_groups, NULL);
759
760     l = fetch_layout(ctx, "l1", NO_VARIANT);
761     /* uvw is invalid, we expect 2 letters, verify it was ignored */
762     iso3166 = rxkb_layout_get_iso3166_first(l);
763     assert(streq(rxkb_iso3166_code_get_code(iso3166), "xz"));
764     assert(rxkb_iso3166_code_next(iso3166) == NULL);
765
766     /* ab is invalid, we expect 3 letters, verify it was ignored */
767     iso639 = rxkb_layout_get_iso639_first(l);
768     assert(streq(rxkb_iso639_code_get_code(iso639), "def"));
769     assert(rxkb_iso639_code_next(iso639) == NULL);
770     rxkb_layout_unref(l);
771
772     rxkb_context_unref(ctx);
773 }
774
775 static void
776 test_popularity(void)
777 {
778     struct test_layout system_layouts[] =  {
779         {"l1", NO_VARIANT },
780         {"l1", "v1" },
781         {NULL},
782     };
783     struct rxkb_context *ctx;
784     struct rxkb_layout *l;
785     const char *ruleset = "xkbtests.extras";
786     char *dir = NULL;
787
788     dir = test_create_rules(ruleset, NULL, system_layouts, NULL);
789     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES |
790                            RXKB_CONTEXT_LOAD_EXOTIC_RULES);
791     assert(ctx);
792     assert(rxkb_context_include_path_append(ctx, dir));
793     /* Hack: rulest above generates xkbtests.extras.xml, loading "xkbtests"
794      * means the extras file counts as exotic */
795     assert(rxkb_context_parse(ctx, "xkbtests"));
796
797     l = fetch_layout(ctx, "l1", NO_VARIANT);
798     assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
799     rxkb_layout_unref(l);
800
801     l = fetch_layout(ctx, "l1", "v1");
802     assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
803     rxkb_layout_unref(l);
804
805     test_remove_rules(dir, ruleset);
806     rxkb_context_unref(ctx);
807 }
808
809
810 static void
811 test_load_merge(void)
812 {
813     struct test_model system_models[] =  {
814         {"m1", "vendor1", "desc1"},
815         {"m2", "vendor2", "desc2"},
816         {NULL},
817     };
818     struct test_model user_models[] =  {
819         {"m3", "vendor3", "desc3"},
820         {"m4", "vendor4", "desc4"},
821         {NULL},
822     };
823     struct test_layout system_layouts[] =  {
824         {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
825         {"l1", "v1", "vbrief1", "vdesc1"},
826         {NULL},
827     };
828     struct test_layout user_layouts[] =  {
829         {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
830         {"l2", "v2", "vbrief2", "vdesc2"},
831         {NULL},
832     };
833     struct test_option_group system_groups[] = {
834         {"grp1", NULL, true,
835           { {"grp1:1"}, {"grp1:2"} } },
836         {"grp2", NULL, false,
837           { {"grp2:1"}, {"grp2:2"} } },
838         { NULL },
839     };
840     struct test_option_group user_groups[] = {
841         {"grp3", NULL, true,
842           { {"grp3:1"}, {"grp3:2"} } },
843         {"grp4", NULL, false,
844           { {"grp4:1"}, {"grp4:2"} } },
845         { NULL },
846     };
847     struct rxkb_context *ctx;
848     struct rxkb_model *m;
849     struct rxkb_layout *l;
850     struct rxkb_option_group *g;
851
852     ctx = test_setup_context(system_models, user_models,
853                              system_layouts, user_layouts,
854                              system_groups, user_groups);
855
856     assert(find_models(ctx, "m1", "m2", "m3", "m4", NULL));
857     assert(find_layouts(ctx, "l1", NO_VARIANT,
858                              "l1", "v1",
859                              "l2", NO_VARIANT,
860                              "l2", "v2", NULL));
861
862     m = fetch_model(ctx, "m1");
863     assert(cmp_models(&system_models[0], m));
864     rxkb_model_unref(m);
865
866     m = fetch_model(ctx, "m2");
867     assert(cmp_models(&system_models[1], m));
868     rxkb_model_unref(m);
869
870     m = fetch_model(ctx, "m3");
871     assert(cmp_models(&user_models[0], m));
872     rxkb_model_unref(m);
873
874     m = fetch_model(ctx, "m4");
875     assert(cmp_models(&user_models[1], m));
876     rxkb_model_unref(m);
877
878     l = fetch_layout(ctx, "l1", NO_VARIANT);
879     assert(cmp_layouts(&system_layouts[0], l));
880     rxkb_layout_unref(l);
881
882     l = fetch_layout(ctx, "l1", "v1");
883     assert(cmp_layouts(&system_layouts[1], l));
884     rxkb_layout_unref(l);
885
886     l = fetch_layout(ctx, "l2", NO_VARIANT);
887     assert(cmp_layouts(&user_layouts[0], l));
888     rxkb_layout_unref(l);
889
890     l = fetch_layout(ctx, "l2", "v2");
891     assert(cmp_layouts(&user_layouts[1], l));
892     rxkb_layout_unref(l);
893
894     g = fetch_option_group(ctx, "grp1");
895     assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
896     rxkb_option_group_unref(g);
897
898     g = fetch_option_group(ctx, "grp2");
899     assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
900     rxkb_option_group_unref(g);
901
902     g = fetch_option_group(ctx, "grp3");
903     assert(cmp_option_groups(&user_groups[0], g, CMP_EXACT));
904     rxkb_option_group_unref(g);
905
906     g = fetch_option_group(ctx, "grp4");
907     assert(cmp_option_groups(&user_groups[1], g, CMP_EXACT));
908     rxkb_option_group_unref(g);
909
910     rxkb_context_unref(ctx);
911 }
912
913 static void
914 test_load_merge_no_overwrite(void)
915 {
916     struct test_model system_models[] =  {
917         {"m1", "vendor1", "desc1"},
918         {"m2", "vendor2", "desc2"},
919         {NULL},
920     };
921     struct test_model user_models[] =  {
922         {"m1", "vendor3", "desc3"}, /* must not overwrite */
923         {"m4", "vendor4", "desc4"},
924         {NULL},
925     };
926     struct test_layout system_layouts[] =  {
927         {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
928         {"l1", "v1", "vbrief1", "vdesc1"},
929         {NULL},
930     };
931     struct test_layout user_layouts[] =  {
932         {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
933         {"l2", "v2", "vbrief2", "vdesc2"},
934         {"l1", NO_VARIANT, "lbrief3", "ldesc3"}, /* must not overwrite */
935         {"l1", "v2", "vbrief3", "vdesc3"}, /* must not overwrite */
936         {NULL},
937     };
938     struct test_option_group system_groups[] = {
939         {"grp1", "gdesc1", true,
940           { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
941         {"grp2", "gdesc2", false,
942           { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
943         { NULL },
944     };
945     struct test_option_group user_groups[] = {
946         {"grp1", "XXXXX", false, /* must not overwrite */
947           { {"grp1:1", "YYYYYYY"}, /* must not overwrite */
948             {"grp1:3", "ZZZZZZ"} } }, /* append */
949         {"grp4", "gdesc4", false,
950           { {"grp4:1", "odesc41"}, {"grp4:2", "odesc42"} } },
951         { NULL },
952     };
953     struct rxkb_context *ctx;
954     struct rxkb_model *m;
955     struct rxkb_layout *l;
956     struct rxkb_option_group *g;
957
958     ctx = test_setup_context(system_models, user_models,
959                              system_layouts, user_layouts,
960                              system_groups, user_groups);
961
962     m = fetch_model(ctx, "m1");
963     assert(cmp_models(&system_models[0], m));
964     rxkb_model_unref(m);
965
966     l = fetch_layout(ctx, "l1", NO_VARIANT);
967     assert(cmp_layouts(&system_layouts[0], l));
968     rxkb_layout_unref(l);
969
970     l = fetch_layout(ctx, "l1", "v1");
971     assert(cmp_layouts(&system_layouts[1], l));
972     rxkb_layout_unref(l);
973
974     assert(find_option(ctx, "grp1", "grp1:3"));
975     g = fetch_option_group(ctx, "grp1");
976     assert(cmp_option_groups(&system_groups[0], g, CMP_MATCHING_ONLY));
977     rxkb_option_group_unref(g);
978
979     rxkb_context_unref(ctx);
980 }
981
982 static void
983 test_no_include_paths(void)
984 {
985     struct rxkb_context *ctx;
986
987     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
988     assert(ctx);
989     assert(!rxkb_context_parse_default_ruleset(ctx));
990
991     rxkb_context_unref(ctx);
992 }
993
994 static void
995 test_invalid_include(void)
996 {
997     struct rxkb_context *ctx;
998
999     ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
1000     assert(ctx);
1001     assert(!rxkb_context_include_path_append(ctx, "/foo/bar/baz/bat"));
1002     assert(!rxkb_context_parse_default_ruleset(ctx));
1003
1004     rxkb_context_unref(ctx);
1005 }
1006
1007 int
1008 main(void)
1009 {
1010     test_no_include_paths();
1011     test_invalid_include();
1012     test_load_basic();
1013     test_load_full();
1014     test_load_merge();
1015     test_load_merge_no_overwrite();
1016     test_load_languages();
1017     test_load_invalid_languages();
1018     test_popularity();
1019
1020     return 0;
1021 }