Commit 255200faa7b2e7e512eb1b171226f36c8e4d3f49

Peter Hutterer 2019-10-28T11:40:56

test: add test for the various default include paths All tests create a temporary directory, set up the environment for that directory and then check the include paths for the presence of that directory, ideally in the right position of the list. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>

diff --git a/test/context.c b/test/context.c
index dc60888..8cb0a47 100644
--- a/test/context.c
+++ b/test/context.c
@@ -26,6 +26,279 @@
 #include "test.h"
 #include "context.h"
 
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+/* keeps a cache of all makedir/maketmpdir directories so we can free and
+ * rmdir them in one go, see unmakedirs() */
+char *dirnames[64];
+int ndirs;
+
+/* keeps a cache of all buffered env vars so we can restore
+ * them in one go, see restore_env() */
+struct env {
+    char *key;
+    char *value;
+} environment[64];
+int nenviron;
+
+static void buffer_env(const char *key)
+{
+    char *v = getenv(key);
+
+    environment[nenviron].key = strdup(key);
+    environment[nenviron].value = v ? strdup(v) : NULL;
+    nenviron++;
+}
+
+static void restore_env(void)
+{
+    for (int i = 0; i < nenviron; i++) {
+        char *key = environment[i].key,
+             *value = environment[i].value;
+
+        if (value)
+            setenv(key, value, 1);
+        else
+            unsetenv(key);
+
+        free(key);
+        free(value);
+    }
+    nenviron = 0;
+    memset(environment, 0, sizeof(environment));
+}
+
+static const char *makedir(const char *parent, const char *path)
+{
+    char *dirname;
+    int err;
+
+    err = asprintf(&dirname, "%s/%s", parent, path);
+    assert(err >= 0);
+    err = mkdir(dirname, 0777);
+    assert(err == 0);
+
+    dirnames[ndirs++] = dirname;
+
+    return dirname;
+}
+
+static const char *maketmpdir(void)
+{
+    const char *template = "/tmp/xkbcommon-test.XXXXXX";
+    char *tmpdir = strdup(template);
+
+    tmpdir = mkdtemp(tmpdir);
+    assert(tmpdir != NULL);
+
+    dirnames[ndirs++] = tmpdir;
+
+    return tmpdir;
+}
+
+static void unmakedirs(void)
+{
+    /* backwards order for rmdir to work */
+    for (int i = ndirs - 1; i >= 0; i--) {
+        char *dir = dirnames[i];
+        if (!dir)
+            break;
+        rmdir(dir);
+        free(dir);
+    }
+    ndirs = 0;
+    memset(dirnames, 0, sizeof(dirnames));
+}
+
+static void
+test_config_root_include_path(void)
+{
+    struct xkb_context *ctx;
+    const char *tmpdir;
+    const char *context_path;
+    int nincludes;
+
+    buffer_env("XKB_CONFIG_ROOT");
+    buffer_env("HOME");
+    buffer_env("XDG_CONFIG_HOME");
+
+    tmpdir = maketmpdir();
+    setenv("XKB_CONFIG_ROOT", tmpdir, 1);
+    unsetenv("HOME");
+    unsetenv("XDG_CONFIG_HOME");
+
+    /* built-in path is last */
+    ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    nincludes = xkb_context_num_include_paths(ctx);
+    assert(nincludes >= 1);
+    context_path = xkb_context_include_path_get(ctx, nincludes - 1);
+    assert(strcmp(context_path, tmpdir) == 0);
+    xkb_context_unref(ctx);
+
+    unmakedirs();
+    restore_env();
+}
+
+static void
+test_config_root_include_path_fallback(void)
+{
+    struct xkb_context *ctx;
+    const char *xkbdir = DFLT_XKB_CONFIG_ROOT;
+    const char *context_path;
+    int nincludes;
+    DIR *dir;
+
+    /* quick and dirty check that the default directory exists.
+     * It may not on a vanilla test box if we just run the test
+     * suite, so where it's not there just skip this test. */
+    dir = opendir(xkbdir);
+    if (!dir)
+        return;
+    closedir(dir);
+
+    buffer_env("XKB_CONFIG_ROOT");
+    buffer_env("HOME");
+    buffer_env("XDG_CONFIG_HOME");
+
+    unsetenv("XKB_CONFIG_ROOT");
+    unsetenv("HOME");
+    unsetenv("XDG_CONFIG_HOME");
+
+    /* built-in path is last */
+    ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    nincludes = xkb_context_num_include_paths(ctx);
+    assert(nincludes >= 1);
+    context_path = xkb_context_include_path_get(ctx, nincludes - 1);
+    assert(strcmp(context_path, xkbdir) == 0);
+    xkb_context_unref(ctx);
+
+    unmakedirs();
+    restore_env();
+}
+
+static void
+test_xkbdir_include_path(void)
+{
+    struct xkb_context *ctx;
+    const char *tmpdir;
+    const char *xkb_path;
+    const char *context_path;
+
+    buffer_env("HOME");
+    buffer_env("XDG_CONFIG_HOME");
+
+    tmpdir = maketmpdir();
+    xkb_path = makedir(tmpdir, ".xkb");
+    setenv("HOME", tmpdir, 1);
+    setenv("XDG_CONFIG_HOME", tmpdir, 1);
+
+    /* No XDG directory in our tmpdir, so we expect
+     * the $HOME/.xkb to be the first include path */
+    ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    assert(xkb_context_num_include_paths(ctx) >= 1);
+    context_path = xkb_context_include_path_get(ctx, 0);
+    assert(strcmp(context_path, xkb_path) == 0);
+    xkb_context_unref(ctx);
+
+    unmakedirs();
+    restore_env();
+}
+
+static void
+test_xdg_include_path(void)
+{
+    struct xkb_context *ctx;
+    const char *tmpdir;
+    const char *xdg_path;
+    const char *context_path;
+
+    buffer_env("XDG_CONFIG_HOME");
+
+    tmpdir = maketmpdir();
+    xdg_path = makedir(tmpdir, "xkb");
+    setenv("XDG_CONFIG_HOME", tmpdir, 1);
+
+    /* XDG path is always first */
+    ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    assert(xkb_context_num_include_paths(ctx) >= 1);
+    context_path = xkb_context_include_path_get(ctx, 0);
+    assert(strcmp(context_path, xdg_path) == 0);
+    xkb_context_unref(ctx);
+
+    unmakedirs();
+    restore_env();
+}
+
+static void
+test_xdg_include_path_fallback(void)
+{
+    struct xkb_context *ctx;
+    const char *tmpdir;
+    const char *xdg_root, *xdg_path;
+    const char *context_path;
+
+    buffer_env("XDG_CONFIG_HOME");
+    buffer_env("HOME");
+
+    tmpdir = maketmpdir();
+    xdg_root = makedir(tmpdir, ".config");
+    xdg_path = makedir(xdg_root, "xkb");
+    setenv("HOME", tmpdir, 1);
+    unsetenv("XDG_CONFIG_HOME");
+
+    /* XDG path is always first, even if fallback */
+    ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    assert(xkb_context_num_include_paths(ctx) >= 1);
+    context_path = xkb_context_include_path_get(ctx, 0);
+    assert(strcmp(context_path, xdg_path) == 0);
+    xkb_context_unref(ctx);
+
+    unmakedirs();
+    restore_env();
+}
+
+static void
+test_include_order(void)
+{
+    struct xkb_context *ctx;
+    const char *tmpdir;
+    const char *xdg_path;
+    const char *xkb_home_path;
+    const char *xkb_root_path;
+    const char *context_path;
+
+    buffer_env("XKB_CONFIG_ROOT");
+    buffer_env("XDG_CONFIG_HOME");
+    buffer_env("HOME");
+
+    tmpdir = maketmpdir();
+    xdg_path = makedir(tmpdir, "xkb");
+    xkb_home_path = makedir(tmpdir, ".xkb");
+    xkb_root_path = makedir(tmpdir, "xkbroot");
+    setenv("HOME", tmpdir, 1);
+    setenv("XDG_CONFIG_HOME", tmpdir, 1);
+    setenv("XKB_CONFIG_ROOT", xkb_root_path, 1);
+
+    ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+    assert(xkb_context_num_include_paths(ctx) >= 3);
+    /* XDG is first */
+    context_path = xkb_context_include_path_get(ctx, 0);
+    assert(strcmp(context_path, xdg_path) == 0);
+    /* $HOME/.xkb is second */
+    context_path = xkb_context_include_path_get(ctx, 1);
+    assert(strcmp(context_path, xkb_home_path) == 0);
+    /* CONFIG_ROOT is last */
+    context_path = xkb_context_include_path_get(ctx, 2);
+    assert(strcmp(context_path, xkb_root_path) == 0);
+
+    xkb_context_unref(ctx);
+
+    unmakedirs();
+    restore_env();
+}
+
 int
 main(void)
 {
@@ -48,5 +321,12 @@ main(void)
 
     xkb_context_unref(context);
 
+    test_config_root_include_path();
+    test_config_root_include_path_fallback();
+    test_xkbdir_include_path();
+    test_xdg_include_path();
+    test_xdg_include_path_fallback();
+    test_include_order();
+
     return 0;
 }