Edit

kc3-lang/libxkbcommon/test/context.c

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2025-10-20 09:29:05
    Hash : f6429933
    Message : context: Add legacy X11 include path for misconfigured setups Some setups use the assumption that the canonical XKB root is always the legacy X11 one, but this is no longer true since xkeyboard-config 2.45, where the X11 path is now a mere symlink to a dedicated data directory for xkeyboard-config. Fixed by using a fallback to the legacy X11 path for misconfigured setups where the canonical XKB root is not available. This fallback can still be skipped if the environment variable `XKB_CONFIG_ROOT` is deliberately set to an empty string.

  • test/context.c
  • /*
     * Copyright © 2012 Intel Corporation
     * SPDX-License-Identifier: MIT
     *
     * Author: Daniel Stone <daniel@fooishbar.org>
     */
    
    #include "config.h"
    
    #include "test.h"
    #include "context.h"
    
    #include <sys/stat.h>
    #include <sys/types.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 = test_makedir(parent, path);
        dirnames[ndirs++] = dirname;
        return dirname;
    }
    
    static const char *maketmpdir(void)
    {
        char *tmpdir = test_maketempdir("xkbcommon-test.XXXXXX");
        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;
    
        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");
    
        /* Valid built-in path is last */
        ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
        const unsigned int 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;
    
        /* 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. */
        struct stat stat_buf;
        int err = stat(xkbdir, &stat_buf);
        if (err != 0)
            return;
        if (!S_ISDIR(stat_buf.st_mode))
            return;
    
        buffer_env("XKB_CONFIG_ROOT");
        buffer_env("HOME");
        buffer_env("XDG_CONFIG_HOME");
    
        unsetenv("XKB_CONFIG_ROOT");
        unsetenv("HOME");
        unsetenv("XDG_CONFIG_HOME");
    
        /* Valid built-in path is last */
        ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
        unsigned int 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);
    
        /* Invalid built-in path is replaced with legacy X11 path */
        setenv("XKB_CONFIG_ROOT", "¡NONSENSE!", 1);
        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, DFLT_XKB_LEGACY_ROOT) == 0);
        xkb_context_unref(ctx);
    
        /* Ensure some path is available */
        const char *tmpdir = maketmpdir();
        setenv("XKB_CONFIG_EXTRA_PATH", tmpdir, 1);
    
        /* Invalid built-in path and deliberately skipped legacy X11 path */
        setenv("XKB_CONFIG_ROOT", "", 1);
        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);
    
        /* No valid path */
        setenv("XKB_CONFIG_EXTRA_PATH", "", 1);
        ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
        assert(!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)
    {
        test_init();
    
        struct xkb_context *context = test_get_context(0);
        xkb_atom_t atom;
    
        assert(context);
    
        assert(xkb_context_num_include_paths(context) == 1);
        assert(!xkb_context_include_path_append(context, "¡NONSENSE!"));
        assert(xkb_context_num_include_paths(context) == 1);
    
        atom = xkb_atom_intern(context, "HELLOjunkjunkjunk", 5);
        assert(atom != XKB_ATOM_NONE);
        assert(streq(xkb_atom_text(context, atom), "HELLO"));
    
        atom = xkb_atom_intern_literal(context, "HELLOjunkjunkjunk");
        assert(atom != XKB_ATOM_NONE);
        assert(streq(xkb_atom_text(context, atom), "HELLOjunkjunkjunk"));
    
        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;
    }