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 const char *iso639[3]; /* language list (iso639 three letter codes), 3 is enough for our test */
60 const char *iso3166[3]; /* country list (iso3166 two letter codes), 3 is enough for our tests */
61 };
62
63 struct test_option {
64 const char *name;
65 const char *description;
66 };
67
68 struct test_option_group {
69 const char *name;
70 const char *description;
71 bool allow_multiple_selection;
72
73 struct test_option options[10];
74 };
75
76 static void
fprint_config_item(FILE * fp,const char * name,const char * vendor,const char * brief,const char * description,const char * const iso639[3],const char * const iso3166[3])77 fprint_config_item(FILE *fp,
78 const char *name,
79 const char *vendor,
80 const char *brief,
81 const char *description,
82 const char * const iso639[3],
83 const char * const iso3166[3])
84 {
85 fprintf(fp, " <configItem>\n"
86 " <name>%s</name>\n", name);
87 if (brief)
88 fprintf(fp, " <shortDescription>%s</shortDescription>\n", brief);
89 if (description)
90 fprintf(fp, " <description>%s</description>\n", description);
91 if (vendor)
92 fprintf(fp, " <vendor>%s</vendor>\n", vendor);
93 if (iso3166 && iso3166[0]) {
94 fprintf(fp, " <countryList>\n");
95 for (int i = 0; i < 3; i++) {
96 const char *iso = iso3166[i];
97 if (!iso)
98 break;
99 fprintf(fp, " <iso3166Id>%s</iso3166Id>\n", iso);
100 }
101 fprintf(fp, " </countryList>\n");
102 }
103 if (iso639 && iso639[0]) {
104 fprintf(fp, " <languageList>\n");
105 for (int i = 0; i < 3; i++) {
106 const char *iso = iso639[i];
107 if (!iso)
108 break;
109 fprintf(fp, " <iso639Id>%s</iso639Id>\n", iso);
110 }
111 fprintf(fp, " </languageList>\n");
112 }
113
114 fprintf(fp, " </configItem>\n");
115 }
116
117 /**
118 * Create a directory populated with a rules/<ruleset>.xml that contains the
119 * given items.
120 *
121 * @return the XKB base directory
122 */
123 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)124 test_create_rules(const char *ruleset,
125 const struct test_model *test_models,
126 const struct test_layout *test_layouts,
127 const struct test_option_group *test_groups)
128 {
129 static int iteration;
130 char *tmpdir;
131 char buf[PATH_MAX];
132 int rc;
133 FILE *fp;
134
135 tmpdir = asprintf_safe("/tmp/%s.%d.XXXXXX", ruleset, iteration++);
136 assert(tmpdir);
137 assert(mkdtemp(tmpdir) == tmpdir);
138
139 rc = snprintf_safe(buf, sizeof(buf), "%s/rules", tmpdir);
140 assert(rc);
141 rc = mkdir(buf, 0777);
142 assert(rc == 0);
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
test_remove_rules(char * basedir,const char * ruleset)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 *
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)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 *
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)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 *
fetch_model(struct rxkb_context * ctx,const char * 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
find_model(struct rxkb_context * ctx,const char * model)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
find_models(struct rxkb_context * ctx,...)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 *
fetch_layout(struct rxkb_context * ctx,const char * layout,const char * variant)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
find_layout(struct rxkb_context * ctx,const char * layout,const char * variant)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
find_layouts(struct rxkb_context * ctx,...)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 *
fetch_option_group(struct rxkb_context * ctx,const char * grp)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
find_option_group(struct rxkb_context * ctx,const char * grp)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 *
fetch_option(struct rxkb_context * ctx,const char * grp,const char * opt)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
find_option(struct rxkb_context * ctx,const char * grp,const char * opt)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
find_options(struct rxkb_context * ctx,...)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
cmp_models(struct test_model * tm,struct rxkb_model * m)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
cmp_layouts(struct test_layout * tl,struct rxkb_layout * l)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
cmp_options(struct test_option * to,struct rxkb_option * o)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
cmp_option_groups(struct test_option_group * tg,struct rxkb_option_group * g,enum cmp_type cmp)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
test_load_basic(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
test_load_full(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
test_load_languages(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
test_load_invalid_languages(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
test_popularity(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
test_load_merge(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
test_load_merge_no_overwrite(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
test_no_include_paths(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
test_invalid_include(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
main(void)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 }
1022