summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2020-05-12 14:09:50 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2020-07-06 15:15:20 +1000
commitafb26e7df9090a0b765eb294b6efff448f763b6f (patch)
treeed8820db131fde2f5190333e0776aced657629d7 /test
parentfe8861338242762da6a3245a106042266280714c (diff)
downloadxorg-lib-libxkbcommon-afb26e7df9090a0b765eb294b6efff448f763b6f.tar.gz
Add libxkbregistry to query available RMLVO
This library is the replacement for clients parsing evdev.xml directly. Instead, they should use the API here so that in the future we may even be able to swap evdev.xml for a more suitable data format. The library parses through evdev.xml (using libxml2) and - if requested - through evdev.extras.xml as well. The merge approach is optimised for the default case where we have a system-installed rules XML and another file in $XDG_CONFIG_DIR that adds a few entries. We load the system file first, then append any custom ones to that. It's not possible to overwrite the MLVO list provided by the system files - if you want to do that, get the change upstream. XML validation is handled through the DTD itself which means we only need to check for a nonempty name, everything else the DTD validation should complain about. The logging system is effectively identical to xkbcommon. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'test')
-rw-r--r--test/registry.c843
1 files changed, 843 insertions, 0 deletions
diff --git a/test/registry.c b/test/registry.c
new file mode 100644
index 0000000..68e74d0
--- /dev/null
+++ b/test/registry.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright © 2020 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "xkbcommon/xkbregistry.h"
+
+#include "utils.h"
+
+#define NO_VARIANT NULL
+
+enum {
+ MODEL = 78,
+ LAYOUT,
+ VARIANT,
+ OPTION,
+};
+
+struct test_model {
+ const char *name; /* required */
+ const char *vendor;
+ const char *description;
+};
+
+struct test_layout {
+ const char *name; /* required */
+ const char *variant;
+ const char *brief;
+ const char *description;
+};
+
+struct test_option {
+ const char *name;
+ const char *description;
+};
+
+struct test_option_group {
+ const char *name;
+ const char *description;
+ bool allow_multiple_selection;
+
+ struct test_option options[10];
+};
+
+static void
+fprint_config_item(FILE *fp,
+ const char *name,
+ const char *vendor,
+ const char *brief,
+ const char *description)
+{
+ fprintf(fp, " <configItem>\n"
+ " <name>%s</name>\n", name);
+ if (brief)
+ fprintf(fp, " <shortDescription>%s</shortDescription>\n", brief);
+ if (description)
+ fprintf(fp, " <description>%s</description>\n", description);
+ if (vendor)
+ fprintf(fp, " <vendor>%s</vendor>\n", vendor);
+ fprintf(fp, " </configItem>\n");
+}
+
+/**
+ * Create a directory populated with a rules/<ruleset>.xml that contains the
+ * given items.
+ *
+ * @return the XKB base directory
+ */
+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)
+{
+ static int iteration;
+ char *tmpdir;
+ char buf[PATH_MAX];
+ int rc;
+ FILE *fp;
+
+ rc = asprintf(&tmpdir, "/tmp/%s.%d.XXXXXX", ruleset, iteration++);
+ assert(rc > 0);
+ assert(mkdtemp(tmpdir) == tmpdir);
+
+ rc = snprintf_safe(buf, sizeof(buf), "%s/rules", tmpdir);
+ assert(rc);
+ rc = mkdir(buf, 0777);
+ assert(rc == 0);
+ rc = snprintf_safe(buf, sizeof(buf), "%s/rules/%s.xml", tmpdir, ruleset);
+ assert(rc);
+
+ fp = fopen(buf, "w");
+ assert(fp);
+
+ fprintf(fp,
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE xkbConfigRegistry SYSTEM \"xkb.dtd\">\n"
+ "<xkbConfigRegistry version=\"1.1\">\n");
+
+ if (test_models) {
+ fprintf(fp, "<modelList>\n");
+
+ for (const struct test_model *m = test_models; m->name; m++) {
+ fprintf(fp, "<model>\n");
+ fprint_config_item(fp, m->name, m->vendor, NULL, m->description);
+ fprintf(fp, "</model>\n");
+ }
+ fprintf(fp, "</modelList>\n");
+ }
+
+ if (test_layouts) {
+ const struct test_layout *l, *next;
+
+ fprintf(fp, "<layoutList>\n");
+
+ l = test_layouts;
+ next = l + 1;
+
+ assert(l->variant == NULL);
+
+ while (l->name) {
+ fprintf(fp, "<layout>\n");
+ fprint_config_item(fp, l->name, NULL, l->brief, l->description);
+
+ if (next->name && streq(next->name, l->name)) {
+ fprintf(fp, "<variantList>\n");
+ do {
+ fprintf(fp, "<variant>\n");
+ fprint_config_item(fp, next->variant, NULL, next->brief,
+ next->description);
+ fprintf(fp, "</variant>\n");
+ l = next;
+ next++;
+ } while (next->name && streq(next->name, l->name));
+ fprintf(fp, "</variantList>\n");
+ }
+ fprintf(fp, "</layout>\n");
+ l++;
+ }
+ fprintf(fp, "</layoutList>\n");
+ }
+
+ if (test_groups) {
+ fprintf(fp, "<optionList>\n");
+
+ for (const struct test_option_group *g = test_groups; g->name; g++) {
+ fprintf(fp, "<group allowMultipleSelection=\"%s\">\n",
+ g->allow_multiple_selection ? "true" : "false");
+ fprint_config_item(fp, g->name, NULL, NULL, g->description);
+ for (const struct test_option *o = g->options; o->name; o++) {
+ fprintf(fp, " <option>\n");
+ fprint_config_item(fp, o->name, NULL, NULL, o->description);
+ fprintf(fp, "</option>\n");
+ }
+ fprintf(fp, "</group>\n");
+ }
+ fprintf(fp, "</optionList>\n");
+ }
+
+ fprintf(fp, "</xkbConfigRegistry>\n");
+ fclose(fp);
+
+ return tmpdir;
+}
+
+static void
+test_remove_rules(char *basedir, const char *ruleset)
+{
+ char path[PATH_MAX];
+ int rc;
+
+ rc = snprintf_safe(path, sizeof(path), "%s/rules/%s.xml", basedir,
+ ruleset);
+ assert(rc);
+ unlink(path);
+ rc = snprintf_safe(path, sizeof(path), "%s/xkb/rules", basedir);
+ assert(rc);
+ rmdir(path);
+ rmdir(basedir);
+ free(basedir);
+}
+
+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)
+{
+ char *sysdir = NULL, *userdir = NULL;
+ struct rxkb_context *ctx;
+
+ sysdir = test_create_rules(ruleset, system_models, system_layouts,
+ system_groups);
+ if (user_models || user_layouts || user_groups)
+ userdir = test_create_rules(ruleset, user_models, user_layouts,
+ user_groups);
+
+ ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
+ assert(ctx);
+ if (userdir)
+ assert(rxkb_context_include_path_append(ctx, userdir));
+ assert(rxkb_context_include_path_append(ctx, sysdir));
+ assert(rxkb_context_parse(ctx, ruleset));
+
+ test_remove_rules(sysdir, ruleset);
+ if (userdir)
+ test_remove_rules(userdir, ruleset);
+
+ return ctx;
+}
+
+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)
+{
+ const char *ruleset = "xkbtests";
+ return test_setup_context_for(ruleset, system_models,
+ user_models, system_layouts,
+ user_layouts, system_groups,
+ user_groups);
+}
+
+static struct rxkb_model *
+fetch_model(struct rxkb_context *ctx, const char *model)
+{
+ struct rxkb_model *m = rxkb_model_first(ctx);
+ while (m) {
+ if (streq(rxkb_model_get_name(m), model))
+ return rxkb_model_ref(m);
+ m = rxkb_model_next(m);
+ }
+ return NULL;
+}
+
+static bool
+find_model(struct rxkb_context *ctx, const char *model)
+{
+ struct rxkb_model *m = fetch_model(ctx, model);
+ rxkb_model_unref(m);
+ return m != NULL;
+}
+
+static bool
+find_models(struct rxkb_context *ctx, ...)
+{
+ va_list args;
+ const char *name;
+ int idx = 0;
+
+ va_start(args, ctx);
+ name = va_arg(args, const char *);
+ while(name) {
+ assert(++idx < 20); /* safety guard */
+ if (!find_model(ctx, name))
+ return false;
+ name = va_arg(args, const char *);
+ };
+
+ va_end(args);
+ return true;
+}
+
+static struct rxkb_layout *
+fetch_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
+{
+ struct rxkb_layout *l = rxkb_layout_first(ctx);
+ while (l) {
+ const char *v = rxkb_layout_get_variant(l);
+
+ if (streq(rxkb_layout_get_name(l), layout) &&
+ ((v == NULL && variant == NULL) ||
+ (v != NULL && variant != NULL && streq(v, variant))))
+ return rxkb_layout_ref(l);
+ l = rxkb_layout_next(l);
+ }
+ return NULL;
+}
+
+static bool
+find_layout(struct rxkb_context *ctx, const char *layout, const char *variant)
+{
+ struct rxkb_layout *l = fetch_layout(ctx, layout, variant);
+ rxkb_layout_unref(l);
+ return l != NULL;
+}
+
+static bool
+find_layouts(struct rxkb_context *ctx, ...)
+{
+ va_list args;
+ const char *name, *variant;
+ int idx = 0;
+
+ va_start(args, ctx);
+ name = va_arg(args, const char *);
+ variant = va_arg(args, const char *);
+ while(name) {
+ assert(++idx < 20); /* safety guard */
+ if (!find_layout(ctx, name, variant))
+ return false;
+ name = va_arg(args, const char *);
+ if (name)
+ variant = va_arg(args, const char *);
+ };
+
+ va_end(args);
+ return true;
+}
+
+static struct rxkb_option_group *
+fetch_option_group(struct rxkb_context *ctx, const char *grp)
+{
+ struct rxkb_option_group *g = rxkb_option_group_first(ctx);
+ while (g) {
+ if (streq(grp, rxkb_option_group_get_name(g)))
+ return rxkb_option_group_ref(g);
+ g = rxkb_option_group_next(g);
+ }
+ return NULL;
+}
+
+static inline bool
+find_option_group(struct rxkb_context *ctx, const char *grp)
+{
+ struct rxkb_option_group *g = fetch_option_group(ctx, grp);
+ rxkb_option_group_unref(g);
+ return g != NULL;
+}
+
+static struct rxkb_option *
+fetch_option(struct rxkb_context *ctx, const char *grp, const char *opt)
+{
+ struct rxkb_option_group *g = rxkb_option_group_first(ctx);
+ while (g) {
+ if (streq(grp, rxkb_option_group_get_name(g))) {
+ struct rxkb_option *o = rxkb_option_first(g);
+
+ while (o) {
+ if (streq(opt, rxkb_option_get_name(o)))
+ return rxkb_option_ref(o);
+ o = rxkb_option_next(o);
+ }
+ }
+ g = rxkb_option_group_next(g);
+ }
+ return NULL;
+}
+
+static bool
+find_option(struct rxkb_context *ctx, const char *grp, const char *opt)
+{
+ struct rxkb_option *o = fetch_option(ctx, grp, opt);
+ rxkb_option_unref(o);
+ return o != NULL;
+}
+
+static bool
+find_options(struct rxkb_context *ctx, ...)
+{
+ va_list args;
+ const char *grp, *opt;
+ int idx = 0;
+
+ va_start(args, ctx);
+ grp = va_arg(args, const char *);
+ opt = va_arg(args, const char *);
+ while(grp) {
+ assert(++idx < 20); /* safety guard */
+ if (!find_option(ctx, grp, opt))
+ return false;
+ grp = va_arg(args, const char *);
+ if (grp)
+ opt = va_arg(args, const char *);
+ };
+
+ va_end(args);
+ return true;
+}
+
+static bool
+cmp_models(struct test_model *tm, struct rxkb_model *m)
+{
+ if (!tm || !m)
+ return false;
+
+ if (!streq(tm->name, rxkb_model_get_name(m)))
+ return false;
+
+ if (!streq_null(tm->vendor, rxkb_model_get_vendor(m)))
+ return false;
+
+ if (!streq_null(tm->description, rxkb_model_get_description(m)))
+ return false;
+
+ return true;
+}
+
+static bool
+cmp_layouts(struct test_layout *tl, struct rxkb_layout *l)
+{
+ if (!tl || !l)
+ return false;
+
+ if (!streq(tl->name, rxkb_layout_get_name(l)))
+ return false;
+
+ if (!streq_null(tl->variant, rxkb_layout_get_variant(l)))
+ return false;
+
+ if (!streq_null(tl->brief, rxkb_layout_get_brief(l)))
+ return false;
+
+ if (!streq_null(tl->description, rxkb_layout_get_description(l)))
+ return false;
+
+ return true;
+}
+
+static bool
+cmp_options(struct test_option *to, struct rxkb_option *o)
+{
+ if (!to || !o)
+ return false;
+
+ if (!streq(to->name, rxkb_option_get_name(o)))
+ return false;
+
+ if (!streq_null(to->description, rxkb_option_get_description(o)))
+ return false;
+
+ return true;
+}
+
+enum cmp_type {
+ CMP_EXACT,
+ CMP_MATCHING_ONLY,
+};
+
+static bool
+cmp_option_groups(struct test_option_group *tg, struct rxkb_option_group *g,
+ enum cmp_type cmp)
+{
+ struct rxkb_option *o;
+ struct test_option *to;
+
+ if (!tg || !g)
+ return false;
+
+ if (!streq(tg->name, rxkb_option_group_get_name(g)))
+ return false;
+
+ if (!streq_null(tg->description, rxkb_option_group_get_description(g)))
+ return false;
+
+ if (tg->allow_multiple_selection != rxkb_option_group_allows_multiple(g))
+ return false;
+
+ to = tg->options;
+ o = rxkb_option_first(g);
+
+ while (o && to->name) {
+ if (!cmp_options(to, o))
+ return false;
+ to++;
+ o = rxkb_option_next(o);
+ }
+
+ if (cmp == CMP_EXACT && (o || to->name))
+ return false;
+
+ return true;
+}
+
+static void
+test_load_basic(void)
+{
+ struct test_model system_models[] = {
+ {"m1"},
+ {"m2"},
+ {NULL},
+ };
+ struct test_layout system_layouts[] = {
+ {"l1"},
+ {"l1", "v1"},
+ {NULL},
+ };
+ struct test_option_group system_groups[] = {
+ {"grp1", NULL, true,
+ { {"grp1:1"}, {"grp1:2"} } },
+ {"grp2", NULL, false,
+ { {"grp2:1"}, {"grp2:2"} } },
+ { NULL },
+ };
+ struct rxkb_context *ctx;
+
+ ctx = test_setup_context(system_models, NULL,
+ system_layouts, NULL,
+ system_groups, NULL);
+
+ assert(find_models(ctx, "m1", "m2", NULL));
+ assert(find_layouts(ctx, "l1", NO_VARIANT,
+ "l1", "v1", NULL));
+ assert(find_options(ctx, "grp1", "grp1:1",
+ "grp1", "grp1:2",
+ "grp2", "grp2:1",
+ "grp2", "grp2:2", NULL));
+ rxkb_context_unref(ctx);
+}
+
+static void
+test_load_full(void)
+{
+ struct test_model system_models[] = {
+ {"m1", "vendor1", "desc1"},
+ {"m2", "vendor2", "desc2"},
+ {NULL},
+ };
+ struct test_layout system_layouts[] = {
+ {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
+ {"l1", "v1", "vbrief1", "vdesc1"},
+ {NULL},
+ };
+ struct test_option_group system_groups[] = {
+ {"grp1", "gdesc1", true,
+ { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
+ {"grp2", "gdesc2", false,
+ { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
+ { NULL },
+ };
+ struct rxkb_context *ctx;
+ struct rxkb_model *m;
+ struct rxkb_layout *l;
+ struct rxkb_option_group *g;
+
+ ctx = test_setup_context(system_models, NULL,
+ system_layouts, NULL,
+ system_groups, NULL);
+
+ m = fetch_model(ctx, "m1");
+ assert(cmp_models(&system_models[0], m));
+ rxkb_model_unref(m);
+
+ m = fetch_model(ctx, "m2");
+ assert(cmp_models(&system_models[1], m));
+ rxkb_model_unref(m);
+
+ l = fetch_layout(ctx, "l1", NO_VARIANT);
+ assert(cmp_layouts(&system_layouts[0], l));
+ rxkb_layout_unref(l);
+
+ l = fetch_layout(ctx, "l1", "v1");
+ assert(cmp_layouts(&system_layouts[1], l));
+ rxkb_layout_unref(l);
+
+ g = fetch_option_group(ctx, "grp1");
+ assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
+ rxkb_option_group_unref(g);
+
+ g = fetch_option_group(ctx, "grp2");
+ assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
+ rxkb_option_group_unref(g);
+
+ rxkb_context_unref(ctx);
+}
+
+static void
+test_popularity(void)
+{
+ struct test_layout system_layouts[] = {
+ {"l1", NO_VARIANT },
+ {"l1", "v1" },
+ {NULL},
+ };
+ struct rxkb_context *ctx;
+ struct rxkb_layout *l;
+ const char *ruleset = "xkbtests.extras";
+ char *dir = NULL;
+
+ dir = test_create_rules(ruleset, NULL, system_layouts, NULL);
+ ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES |
+ RXKB_CONTEXT_LOAD_EXOTIC_RULES);
+ assert(ctx);
+ assert(rxkb_context_include_path_append(ctx, dir));
+ /* Hack: rulest above generates xkbtests.extras.xml, loading "xkbtests"
+ * means the extras file counts as exotic */
+ assert(rxkb_context_parse(ctx, "xkbtests"));
+
+ l = fetch_layout(ctx, "l1", NO_VARIANT);
+ assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
+ rxkb_layout_unref(l);
+
+ l = fetch_layout(ctx, "l1", "v1");
+ assert(rxkb_layout_get_popularity(l) == RXKB_POPULARITY_EXOTIC);
+ rxkb_layout_unref(l);
+
+ test_remove_rules(dir, ruleset);
+ rxkb_context_unref(ctx);
+}
+
+
+static void
+test_load_merge(void)
+{
+ struct test_model system_models[] = {
+ {"m1", "vendor1", "desc1"},
+ {"m2", "vendor2", "desc2"},
+ {NULL},
+ };
+ struct test_model user_models[] = {
+ {"m3", "vendor3", "desc3"},
+ {"m4", "vendor4", "desc4"},
+ {NULL},
+ };
+ struct test_layout system_layouts[] = {
+ {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
+ {"l1", "v1", "vbrief1", "vdesc1"},
+ {NULL},
+ };
+ struct test_layout user_layouts[] = {
+ {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
+ {"l2", "v2", "vbrief2", "vdesc2"},
+ {NULL},
+ };
+ struct test_option_group system_groups[] = {
+ {"grp1", NULL, true,
+ { {"grp1:1"}, {"grp1:2"} } },
+ {"grp2", NULL, false,
+ { {"grp2:1"}, {"grp2:2"} } },
+ { NULL },
+ };
+ struct test_option_group user_groups[] = {
+ {"grp3", NULL, true,
+ { {"grp3:1"}, {"grp3:2"} } },
+ {"grp4", NULL, false,
+ { {"grp4:1"}, {"grp4:2"} } },
+ { NULL },
+ };
+ struct rxkb_context *ctx;
+ struct rxkb_model *m;
+ struct rxkb_layout *l;
+ struct rxkb_option_group *g;
+
+ ctx = test_setup_context(system_models, user_models,
+ system_layouts, user_layouts,
+ system_groups, user_groups);
+
+ assert(find_models(ctx, "m1", "m2", "m3", "m4", NULL));
+ assert(find_layouts(ctx, "l1", NO_VARIANT,
+ "l1", "v1",
+ "l2", NO_VARIANT,
+ "l2", "v2", NULL));
+
+ m = fetch_model(ctx, "m1");
+ assert(cmp_models(&system_models[0], m));
+ rxkb_model_unref(m);
+
+ m = fetch_model(ctx, "m2");
+ assert(cmp_models(&system_models[1], m));
+ rxkb_model_unref(m);
+
+ m = fetch_model(ctx, "m3");
+ assert(cmp_models(&user_models[0], m));
+ rxkb_model_unref(m);
+
+ m = fetch_model(ctx, "m4");
+ assert(cmp_models(&user_models[1], m));
+ rxkb_model_unref(m);
+
+ l = fetch_layout(ctx, "l1", NO_VARIANT);
+ assert(cmp_layouts(&system_layouts[0], l));
+ rxkb_layout_unref(l);
+
+ l = fetch_layout(ctx, "l1", "v1");
+ assert(cmp_layouts(&system_layouts[1], l));
+ rxkb_layout_unref(l);
+
+ l = fetch_layout(ctx, "l2", NO_VARIANT);
+ assert(cmp_layouts(&user_layouts[0], l));
+ rxkb_layout_unref(l);
+
+ l = fetch_layout(ctx, "l2", "v2");
+ assert(cmp_layouts(&user_layouts[1], l));
+ rxkb_layout_unref(l);
+
+ g = fetch_option_group(ctx, "grp1");
+ assert(cmp_option_groups(&system_groups[0], g, CMP_EXACT));
+ rxkb_option_group_unref(g);
+
+ g = fetch_option_group(ctx, "grp2");
+ assert(cmp_option_groups(&system_groups[1], g, CMP_EXACT));
+ rxkb_option_group_unref(g);
+
+ g = fetch_option_group(ctx, "grp3");
+ assert(cmp_option_groups(&user_groups[0], g, CMP_EXACT));
+ rxkb_option_group_unref(g);
+
+ g = fetch_option_group(ctx, "grp4");
+ assert(cmp_option_groups(&user_groups[1], g, CMP_EXACT));
+ rxkb_option_group_unref(g);
+
+ rxkb_context_unref(ctx);
+}
+
+static void
+test_load_merge_no_overwrite(void)
+{
+ struct test_model system_models[] = {
+ {"m1", "vendor1", "desc1"},
+ {"m2", "vendor2", "desc2"},
+ {NULL},
+ };
+ struct test_model user_models[] = {
+ {"m1", "vendor3", "desc3"}, /* must not overwrite */
+ {"m4", "vendor4", "desc4"},
+ {NULL},
+ };
+ struct test_layout system_layouts[] = {
+ {"l1", NO_VARIANT, "lbrief1", "ldesc1"},
+ {"l1", "v1", "vbrief1", "vdesc1"},
+ {NULL},
+ };
+ struct test_layout user_layouts[] = {
+ {"l2", NO_VARIANT, "lbrief2", "ldesc2"},
+ {"l2", "v2", "vbrief2", "vdesc2"},
+ {"l1", NO_VARIANT, "lbrief3", "ldesc3"}, /* must not overwrite */
+ {"l1", "v2", "vbrief3", "vdesc3"}, /* must not overwrite */
+ {NULL},
+ };
+ struct test_option_group system_groups[] = {
+ {"grp1", "gdesc1", true,
+ { {"grp1:1", "odesc11"}, {"grp1:2", "odesc12"} } },
+ {"grp2", "gdesc2", false,
+ { {"grp2:1", "odesc21"}, {"grp2:2", "odesc22"} } },
+ { NULL },
+ };
+ struct test_option_group user_groups[] = {
+ {"grp1", "XXXXX", false, /* must not overwrite */
+ { {"grp1:1", "YYYYYYY"}, /* must not overwrite */
+ {"grp1:3", "ZZZZZZ"} } }, /* append */
+ {"grp4", "gdesc4", false,
+ { {"grp4:1", "odesc41"}, {"grp4:2", "odesc42"} } },
+ { NULL },
+ };
+ struct rxkb_context *ctx;
+ struct rxkb_model *m;
+ struct rxkb_layout *l;
+ struct rxkb_option_group *g;
+
+ ctx = test_setup_context(system_models, user_models,
+ system_layouts, user_layouts,
+ system_groups, user_groups);
+
+ m = fetch_model(ctx, "m1");
+ assert(cmp_models(&system_models[0], m));
+ rxkb_model_unref(m);
+
+ l = fetch_layout(ctx, "l1", NO_VARIANT);
+ assert(cmp_layouts(&system_layouts[0], l));
+ rxkb_layout_unref(l);
+
+ l = fetch_layout(ctx, "l1", "v1");
+ assert(cmp_layouts(&system_layouts[1], l));
+ rxkb_layout_unref(l);
+
+ assert(find_option(ctx, "grp1", "grp1:3"));
+ g = fetch_option_group(ctx, "grp1");
+ assert(cmp_option_groups(&system_groups[0], g, CMP_MATCHING_ONLY));
+ rxkb_option_group_unref(g);
+
+ rxkb_context_unref(ctx);
+}
+
+static void
+test_no_include_paths(void)
+{
+ struct rxkb_context *ctx;
+
+ ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
+ assert(ctx);
+ assert(!rxkb_context_parse_default_ruleset(ctx));
+
+ rxkb_context_unref(ctx);
+}
+
+static void
+test_invalid_include(void)
+{
+ struct rxkb_context *ctx;
+
+ ctx = rxkb_context_new(RXKB_CONTEXT_NO_DEFAULT_INCLUDES);
+ assert(ctx);
+ assert(!rxkb_context_include_path_append(ctx, "/foo/bar/baz/bat"));
+ assert(!rxkb_context_parse_default_ruleset(ctx));
+
+ rxkb_context_unref(ctx);
+}
+
+int
+main(void)
+{
+ test_no_include_paths();
+ test_invalid_include();
+ test_load_basic();
+ test_load_full();
+ test_load_merge();
+ test_load_merge_no_overwrite();
+ test_popularity();
+
+ return 0;
+}