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
fprint_config_item(FILE * fp,const char * name,const char * vendor,const char * brief,const char * description)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 *
test_create_rules(const char * ruleset,const struct test_model * test_models,const struct test_layout * test_layouts,const struct test_option_group * test_groups)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
test_remove_rules(char * basedir,const char * ruleset)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 *
test_setup_context_for(const char * ruleset,struct test_model * system_models,struct test_model * user_models,struct test_layout * system_layouts,struct test_layout * user_layouts,struct test_option_group * system_groups,struct test_option_group * user_groups)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 *
test_setup_context(struct test_model * system_models,struct test_model * user_models,struct test_layout * system_layouts,struct test_layout * user_layouts,struct test_option_group * system_groups,struct test_option_group * user_groups)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 *
fetch_model(struct rxkb_context * ctx,const char * 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
find_model(struct rxkb_context * ctx,const char * model)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
find_models(struct rxkb_context * ctx,...)280 find_models(struct rxkb_context *ctx, ...)
281 {
282 va_list args;
283 const char *name;
284 int idx = 0;
285
286 va_start(args, ctx);
287 name = va_arg(args, const char *);
288 while(name) {
289 assert(++idx < 20); /* safety guard */
290 if (!find_model(ctx, name))
291 return false;
292 name = va_arg(args, const char *);
293 };
294
295 va_end(args);
296 return true;
297 }
298
299 static struct rxkb_layout *
fetch_layout(struct rxkb_context * ctx,const char * layout,const char * variant)300 fetch_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
301 {
302 struct rxkb_layout *l = rxkb_layout_first(ctx);
303 while (l) {
304 const char *v = rxkb_layout_get_variant(l);
305
306 if (streq(rxkb_layout_get_name(l), layout) &&
307 ((v == NULL && variant == NULL) ||
308 (v != NULL && variant != NULL && streq(v, variant))))
309 return rxkb_layout_ref(l);
310 l = rxkb_layout_next(l);
311 }
312 return NULL;
313 }
314
315 static bool
find_layout(struct rxkb_context * ctx,const char * layout,const char * variant)316 find_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
317 {
318 struct rxkb_layout *l = fetch_layout(ctx, layout, variant);
319 rxkb_layout_unref(l);
320 return l != NULL;
321 }
322
323 static bool
find_layouts(struct rxkb_context * ctx,...)324 find_layouts(struct rxkb_context *ctx, ...)
325 {
326 va_list args;
327 const char *name, *variant;
328 int idx = 0;
329
330 va_start(args, ctx);
331 name = va_arg(args, const char *);
332 variant = va_arg(args, const char *);
333 while(name) {
334 assert(++idx < 20); /* safety guard */
335 if (!find_layout(ctx, name, variant))
336 return false;
337 name = va_arg(args, const char *);
338 if (name)
339 variant = va_arg(args, const char *);
340 };
341
342 va_end(args);
343 return true;
344 }
345
346 static struct rxkb_option_group *
fetch_option_group(struct rxkb_context * ctx,const char * grp)347 fetch_option_group(struct rxkb_context *ctx, const char *grp)
348 {
349 struct rxkb_option_group *g = rxkb_option_group_first(ctx);
350 while (g) {
351 if (streq(grp, rxkb_option_group_get_name(g)))
352 return rxkb_option_group_ref(g);
353 g = rxkb_option_group_next(g);
354 }
355 return NULL;
356 }
357
358 static inline bool
find_option_group(struct rxkb_context * ctx,const char * grp)359 find_option_group(struct rxkb_context *ctx, const char *grp)
360 {
361 struct rxkb_option_group *g = fetch_option_group(ctx, grp);
362 rxkb_option_group_unref(g);
363 return g != NULL;
364 }
365
366 static struct rxkb_option *
fetch_option(struct rxkb_context * ctx,const char * grp,const char * opt)367 fetch_option(struct rxkb_context *ctx, const char *grp, const char *opt)
368 {
369 struct rxkb_option_group *g = rxkb_option_group_first(ctx);
370 while (g) {
371 if (streq(grp, rxkb_option_group_get_name(g))) {
372 struct rxkb_option *o = rxkb_option_first(g);
373
374 while (o) {
375 if (streq(opt, rxkb_option_get_name(o)))
376 return rxkb_option_ref(o);
377 o = rxkb_option_next(o);
378 }
379 }
380 g = rxkb_option_group_next(g);
381 }
382 return NULL;
383 }
384
385 static bool
find_option(struct rxkb_context * ctx,const char * grp,const char * opt)386 find_option(struct rxkb_context *ctx, const char *grp, const char *opt)
387 {
388 struct rxkb_option *o = fetch_option(ctx, grp, opt);
389 rxkb_option_unref(o);
390 return o != NULL;
391 }
392
393 static bool
find_options(struct rxkb_context * ctx,...)394 find_options(struct rxkb_context *ctx, ...)
395 {
396 va_list args;
397 const char *grp, *opt;
398 int idx = 0;
399
400 va_start(args, ctx);
401 grp = va_arg(args, const char *);
402 opt = va_arg(args, const char *);
403 while(grp) {
404 assert(++idx < 20); /* safety guard */
405 if (!find_option(ctx, grp, opt))
406 return false;
407 grp = va_arg(args, const char *);
408 if (grp)
409 opt = va_arg(args, const char *);
410 };
411
412 va_end(args);
413 return true;
414 }
415
416 static bool
cmp_models(struct test_model * tm,struct rxkb_model * m)417 cmp_models(struct test_model *tm, struct rxkb_model *m)
418 {
419 if (!tm || !m)
420 return false;
421
422 if (!streq(tm->name, rxkb_model_get_name(m)))
423 return false;
424
425 if (!streq_null(tm->vendor, rxkb_model_get_vendor(m)))
426 return false;
427
428 if (!streq_null(tm->description, rxkb_model_get_description(m)))
429 return false;
430
431 return true;
432 }
433
434 static bool
cmp_layouts(struct test_layout * tl,struct rxkb_layout * l)435 cmp_layouts(struct test_layout *tl, struct rxkb_layout *l)
436 {
437 if (!tl || !l)
438 return false;
439
440 if (!streq(tl->name, rxkb_layout_get_name(l)))
441 return false;
442
443 if (!streq_null(tl->variant, rxkb_layout_get_variant(l)))
444 return false;
445
446 if (!streq_null(tl->brief, rxkb_layout_get_brief(l)))
447 return false;
448
449 if (!streq_null(tl->description, rxkb_layout_get_description(l)))
450 return false;
451
452 return true;
453 }
454
455 static bool
cmp_options(struct test_option * to,struct rxkb_option * o)456 cmp_options(struct test_option *to, struct rxkb_option *o)
457 {
458 if (!to || !o)
459 return false;
460
461 if (!streq(to->name, rxkb_option_get_name(o)))
462 return false;
463
464 if (!streq_null(to->description, rxkb_option_get_description(o)))
465 return false;
466
467 return true;
468 }
469
470 enum cmp_type {
471 CMP_EXACT,
472 CMP_MATCHING_ONLY,
473 };
474
475 static bool
cmp_option_groups(struct test_option_group * tg,struct rxkb_option_group * g,enum cmp_type cmp)476 cmp_option_groups(struct test_option_group *tg, struct rxkb_option_group *g,
477 enum cmp_type cmp)
478 {
479 struct rxkb_option *o;
480 struct test_option *to;
481
482 if (!tg || !g)
483 return false;
484
485 if (!streq(tg->name, rxkb_option_group_get_name(g)))
486 return false;
487
488 if (!streq_null(tg->description, rxkb_option_group_get_description(g)))
489 return false;
490
491 if (tg->allow_multiple_selection != rxkb_option_group_allows_multiple(g))
492 return false;
493
494 to = tg->options;
495 o = rxkb_option_first(g);
496
497 while (o && to->name) {
498 if (!cmp_options(to, o))
499 return false;
500 to++;
501 o = rxkb_option_next(o);
502 }
503
504 if (cmp == CMP_EXACT && (o || to->name))
505 return false;
506
507 return true;
508 }
509
510 static void
test_load_basic(void)511 test_load_basic(void)
512 {
513 struct test_model system_models[] = {
514 {"m1"},
515 {"m2"},
516 {NULL},
517 };
518 struct test_layout system_layouts[] = {
519 {"l1"},
520 {"l1", "v1"},
521 {NULL},
522 };
523 struct test_option_group system_groups[] = {
524 {"grp1", NULL, true,
525 { {"grp1:1"}, {"grp1:2"} } },
526 {"grp2", NULL, false,
527 { {"grp2:1"}, {"grp2:2"} } },
528 { NULL },
529 };
530 struct rxkb_context *ctx;
531
532 ctx = test_setup_context(system_models, NULL,
533 system_layouts, NULL,
534 system_groups, NULL);
535
536 assert(find_models(ctx, "m1", "m2", NULL));
537 assert(find_layouts(ctx, "l1", NO_VARIANT,
538 "l1", "v1", NULL));
539 assert(find_options(ctx, "grp1", "grp1:1",
540 "grp1", "grp1:2",
541 "grp2", "grp2:1",
542 "grp2", "grp2:2", NULL));
543 rxkb_context_unref(ctx);
544 }
545
546 static void
test_load_full(void)547 test_load_full(void)
548 {
549 struct test_model system_models[] = {
550 {"m1", "vendor1", "desc1"},
551 {"m2", "vendor2", "desc2"},
552 {NULL},
553 };
554 struct test_layout system_layouts[] = {
555 {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
556 {"l1", "v1", "vbrief1", "vdesc1"},
557 {NULL},
558 };
559 struct test_option_group system_groups[] = {
560 {"grp1", "gdesc1", true,
561 { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
562 {"grp2", "gdesc2", false,
563 { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
564 { NULL },
565 };
566 struct rxkb_context *ctx;
567 struct rxkb_model *m;
568 struct rxkb_layout *l;
569 struct rxkb_option_group *g;
570
571 ctx = test_setup_context(system_models, NULL,
572 system_layouts, NULL,
573 system_groups, NULL);
574
575 m = fetch_model(ctx, "m1");
576 assert(cmp_models(&system_models[0], m));
577 rxkb_model_unref(m);
578
579 m = fetch_model(ctx, "m2");
580 assert(cmp_models(&system_models[1], m));
581 rxkb_model_unref(m);
582
583 l = fetch_layout(ctx, "l1", NO_VARIANT);
584 assert(cmp_layouts(&system_layouts[0], l));
585 rxkb_layout_unref(l);
586
587 l = fetch_layout(ctx, "l1", "v1");
588 assert(cmp_layouts(&system_layouts[1], l));
589 rxkb_layout_unref(l);
590
591 g = fetch_option_group(ctx, "grp1");
592 assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
593 rxkb_option_group_unref(g);
594
595 g = fetch_option_group(ctx, "grp2");
596 assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
597 rxkb_option_group_unref(g);
598
599 rxkb_context_unref(ctx);
600 }
601
602 static void
test_popularity(void)603 test_popularity(void)
604 {
605 struct test_layout system_layouts[] = {
606 {"l1", NO_VARIANT },
607 {"l1", "v1" },
608 {NULL},
609 };
610 struct rxkb_context *ctx;
611 struct rxkb_layout *l;
612 const char *ruleset = "xkbtests.extras";
613 char *dir = NULL;
614
615 dir = test_create_rules(ruleset, NULL, system_layouts, NULL);
616 ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES |
617 RXKB_CONTEXT_LOAD_EXOTIC_RULES);
618 assert(ctx);
619 assert(rxkb_context_include_path_append(ctx, dir));
620 /* Hack: rulest above generates xkbtests.extras.xml, loading "xkbtests"
621 * means the extras file counts as exotic */
622 assert(rxkb_context_parse(ctx, "xkbtests"));
623
624 l = fetch_layout(ctx, "l1", NO_VARIANT);
625 assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
626 rxkb_layout_unref(l);
627
628 l = fetch_layout(ctx, "l1", "v1");
629 assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
630 rxkb_layout_unref(l);
631
632 test_remove_rules(dir, ruleset);
633 rxkb_context_unref(ctx);
634 }
635
636
637 static void
test_load_merge(void)638 test_load_merge(void)
639 {
640 struct test_model system_models[] = {
641 {"m1", "vendor1", "desc1"},
642 {"m2", "vendor2", "desc2"},
643 {NULL},
644 };
645 struct test_model user_models[] = {
646 {"m3", "vendor3", "desc3"},
647 {"m4", "vendor4", "desc4"},
648 {NULL},
649 };
650 struct test_layout system_layouts[] = {
651 {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
652 {"l1", "v1", "vbrief1", "vdesc1"},
653 {NULL},
654 };
655 struct test_layout user_layouts[] = {
656 {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
657 {"l2", "v2", "vbrief2", "vdesc2"},
658 {NULL},
659 };
660 struct test_option_group system_groups[] = {
661 {"grp1", NULL, true,
662 { {"grp1:1"}, {"grp1:2"} } },
663 {"grp2", NULL, false,
664 { {"grp2:1"}, {"grp2:2"} } },
665 { NULL },
666 };
667 struct test_option_group user_groups[] = {
668 {"grp3", NULL, true,
669 { {"grp3:1"}, {"grp3:2"} } },
670 {"grp4", NULL, false,
671 { {"grp4:1"}, {"grp4:2"} } },
672 { NULL },
673 };
674 struct rxkb_context *ctx;
675 struct rxkb_model *m;
676 struct rxkb_layout *l;
677 struct rxkb_option_group *g;
678
679 ctx = test_setup_context(system_models, user_models,
680 system_layouts, user_layouts,
681 system_groups, user_groups);
682
683 assert(find_models(ctx, "m1", "m2", "m3", "m4", NULL));
684 assert(find_layouts(ctx, "l1", NO_VARIANT,
685 "l1", "v1",
686 "l2", NO_VARIANT,
687 "l2", "v2", NULL));
688
689 m = fetch_model(ctx, "m1");
690 assert(cmp_models(&system_models[0], m));
691 rxkb_model_unref(m);
692
693 m = fetch_model(ctx, "m2");
694 assert(cmp_models(&system_models[1], m));
695 rxkb_model_unref(m);
696
697 m = fetch_model(ctx, "m3");
698 assert(cmp_models(&user_models[0], m));
699 rxkb_model_unref(m);
700
701 m = fetch_model(ctx, "m4");
702 assert(cmp_models(&user_models[1], m));
703 rxkb_model_unref(m);
704
705 l = fetch_layout(ctx, "l1", NO_VARIANT);
706 assert(cmp_layouts(&system_layouts[0], l));
707 rxkb_layout_unref(l);
708
709 l = fetch_layout(ctx, "l1", "v1");
710 assert(cmp_layouts(&system_layouts[1], l));
711 rxkb_layout_unref(l);
712
713 l = fetch_layout(ctx, "l2", NO_VARIANT);
714 assert(cmp_layouts(&user_layouts[0], l));
715 rxkb_layout_unref(l);
716
717 l = fetch_layout(ctx, "l2", "v2");
718 assert(cmp_layouts(&user_layouts[1], l));
719 rxkb_layout_unref(l);
720
721 g = fetch_option_group(ctx, "grp1");
722 assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
723 rxkb_option_group_unref(g);
724
725 g = fetch_option_group(ctx, "grp2");
726 assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
727 rxkb_option_group_unref(g);
728
729 g = fetch_option_group(ctx, "grp3");
730 assert(cmp_option_groups(&user_groups[0], g, CMP_EXACT));
731 rxkb_option_group_unref(g);
732
733 g = fetch_option_group(ctx, "grp4");
734 assert(cmp_option_groups(&user_groups[1], g, CMP_EXACT));
735 rxkb_option_group_unref(g);
736
737 rxkb_context_unref(ctx);
738 }
739
740 static void
test_load_merge_no_overwrite(void)741 test_load_merge_no_overwrite(void)
742 {
743 struct test_model system_models[] = {
744 {"m1", "vendor1", "desc1"},
745 {"m2", "vendor2", "desc2"},
746 {NULL},
747 };
748 struct test_model user_models[] = {
749 {"m1", "vendor3", "desc3"}, /* must not overwrite */
750 {"m4", "vendor4", "desc4"},
751 {NULL},
752 };
753 struct test_layout system_layouts[] = {
754 {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
755 {"l1", "v1", "vbrief1", "vdesc1"},
756 {NULL},
757 };
758 struct test_layout user_layouts[] = {
759 {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
760 {"l2", "v2", "vbrief2", "vdesc2"},
761 {"l1", NO_VARIANT, "lbrief3", "ldesc3"}, /* must not overwrite */
762 {"l1", "v2", "vbrief3", "vdesc3"}, /* must not overwrite */
763 {NULL},
764 };
765 struct test_option_group system_groups[] = {
766 {"grp1", "gdesc1", true,
767 { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
768 {"grp2", "gdesc2", false,
769 { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
770 { NULL },
771 };
772 struct test_option_group user_groups[] = {
773 {"grp1", "XXXXX", false, /* must not overwrite */
774 { {"grp1:1", "YYYYYYY"}, /* must not overwrite */
775 {"grp1:3", "ZZZZZZ"} } }, /* append */
776 {"grp4", "gdesc4", false,
777 { {"grp4:1", "odesc41"}, {"grp4:2", "odesc42"} } },
778 { NULL },
779 };
780 struct rxkb_context *ctx;
781 struct rxkb_model *m;
782 struct rxkb_layout *l;
783 struct rxkb_option_group *g;
784
785 ctx = test_setup_context(system_models, user_models,
786 system_layouts, user_layouts,
787 system_groups, user_groups);
788
789 m = fetch_model(ctx, "m1");
790 assert(cmp_models(&system_models[0], m));
791 rxkb_model_unref(m);
792
793 l = fetch_layout(ctx, "l1", NO_VARIANT);
794 assert(cmp_layouts(&system_layouts[0], l));
795 rxkb_layout_unref(l);
796
797 l = fetch_layout(ctx, "l1", "v1");
798 assert(cmp_layouts(&system_layouts[1], l));
799 rxkb_layout_unref(l);
800
801 assert(find_option(ctx, "grp1", "grp1:3"));
802 g = fetch_option_group(ctx, "grp1");
803 assert(cmp_option_groups(&system_groups[0], g, CMP_MATCHING_ONLY));
804 rxkb_option_group_unref(g);
805
806 rxkb_context_unref(ctx);
807 }
808
809 static void
test_no_include_paths(void)810 test_no_include_paths(void)
811 {
812 struct rxkb_context *ctx;
813
814 ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
815 assert(ctx);
816 assert(!rxkb_context_parse_default_ruleset(ctx));
817
818 rxkb_context_unref(ctx);
819 }
820
821 static void
test_invalid_include(void)822 test_invalid_include(void)
823 {
824 struct rxkb_context *ctx;
825
826 ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
827 assert(ctx);
828 assert(!rxkb_context_include_path_append(ctx, "/foo/bar/baz/bat"));
829 assert(!rxkb_context_parse_default_ruleset(ctx));
830
831 rxkb_context_unref(ctx);
832 }
833
834 int
main(void)835 main(void)
836 {
837 test_no_include_paths();
838 test_invalid_include();
839 test_load_basic();
840 test_load_full();
841 test_load_merge();
842 test_load_merge_no_overwrite();
843 test_popularity();
844
845 return 0;
846 }
847