Edit

kc3-lang/libxkbcommon/test/log.c

Branch :

  • Show log

    Commit

  • Author : Pierre Le Marre
    Date : 2025-06-09 16:26:56
    Hash : 2acf5eca
    Message : test: Use explicit keymap format in test_compile_buffer()

  • test/log.c
  • /*
     * Copyright © 2012 Ran Benita <ran234@gmail.com>
     * SPDX-License-Identifier: MIT
     */
    
    #include "config.h"
    
    #include <locale.h>
    
    #include "test.h"
    #include "context.h"
    #include "messages-codes.h"
    
    #ifdef __GNUC__
    #pragma GCC diagnostic ignored "-Wmissing-format-attribute"
    #endif
    
    static const char *
    log_level_to_string(enum xkb_log_level level)
    {
        switch (level) {
        case XKB_LOG_LEVEL_CRITICAL:
            return "critical";
        case XKB_LOG_LEVEL_ERROR:
            return "error";
        case XKB_LOG_LEVEL_WARNING:
            return "warning";
        case XKB_LOG_LEVEL_INFO:
            return "info";
        case XKB_LOG_LEVEL_DEBUG:
            return "debug";
        }
    
        return "unknown";
    }
    
    ATTR_PRINTF(3, 0) static void
    log_fn(struct xkb_context *ctx, enum xkb_log_level level,
           const char *fmt, va_list args)
    {
        char *s;
        int size;
        darray_char *ls = xkb_context_get_user_data(ctx);
        assert(ls);
    
        size = vasprintf(&s, fmt, args);
        assert(size != -1);
    
        darray_append_string(*ls, log_level_to_string(level));
        darray_append_lit(*ls, ": ");
        darray_append_string(*ls, s);
        free(s);
    }
    
    static void
    test_basic(void)
    {
        int ret;
        ret = setenv("XKB_LOG_LEVEL", "warn", 1);
        assert(ret == 0);
        ret = setenv("XKB_LOG_VERBOSITY", "5", 1);
        assert(ret == 0);
    
        struct xkb_context *ctx = test_get_context(CONTEXT_NO_FLAG);
        assert(ctx);
    
        darray_char log_string;
        darray_init(log_string);
        xkb_context_set_user_data(ctx, &log_string);
        xkb_context_set_log_fn(ctx, log_fn);
    
        log_warn(ctx, XKB_LOG_MESSAGE_NO_ID, "first warning: %d\n", 87);
        log_info(ctx, XKB_LOG_MESSAGE_NO_ID, "first info\n");
        log_dbg(ctx, XKB_LOG_MESSAGE_NO_ID, "first debug: %s\n", "hello");
        log_err(ctx, XKB_LOG_MESSAGE_NO_ID, "first error: %lu\n", 115415UL);
        log_vrb(ctx, 5, XKB_LOG_MESSAGE_NO_ID, "first verbose 5\n");
    
        xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_DEBUG);
        log_warn(ctx, XKB_LOG_MESSAGE_NO_ID, "second warning: %d\n", 87);
        log_dbg(ctx, XKB_LOG_MESSAGE_NO_ID, "second debug: %s %s\n", "hello", "world");
        log_info(ctx, XKB_LOG_MESSAGE_NO_ID, "second info\n");
        log_err(ctx, XKB_ERROR_MALFORMED_NUMBER_LITERAL, "second error: %lu\n", 115415UL);
        log_vrb(ctx, 6, XKB_LOG_MESSAGE_NO_ID, "second verbose 6\n");
    
        xkb_context_set_log_verbosity(ctx, 0);
        xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_CRITICAL);
        log_warn(ctx, XKB_LOG_MESSAGE_NO_ID, "third warning: %d\n", 87);
        log_dbg(ctx, XKB_LOG_MESSAGE_NO_ID, "third debug: %s %s\n", "hello", "world");
        log_info(ctx, XKB_LOG_MESSAGE_NO_ID, "third info\n");
        log_err(ctx, XKB_LOG_MESSAGE_NO_ID, "third error: %lu\n", 115415UL);
        log_vrb(ctx, 0, XKB_LOG_MESSAGE_NO_ID, "third verbose 0\n");
    
        printf("%s", darray_items(log_string));
    
        assert(streq(darray_items(log_string),
                     "warning: first warning: 87\n"
                     "error: first error: 115415\n"
                     "warning: first verbose 5\n"
                     "warning: second warning: 87\n"
                     "debug: second debug: hello world\n"
                     "info: second info\n"
                     "error: [XKB-034] second error: 115415\n"));
    
        xkb_context_unref(ctx);
        darray_free(log_string);
    }
    
    struct test_data {
        const char * const input;
        const char * const log;
        bool error;
    };
    
    static void
    test_keymaps(void)
    {
        struct xkb_context *ctx = test_get_context(CONTEXT_NO_FLAG);
        assert(ctx);
    
        darray_char log_string;
        darray_init(log_string);
        xkb_context_set_user_data(ctx, &log_string);
        xkb_context_set_log_fn(ctx, log_fn);
    
        xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_WARNING);
        xkb_context_set_log_verbosity(ctx, 10);
    
        const struct test_data keymaps[] = {
            {
                .input = "",
                .log = "error: [XKB-822] Failed to parse input xkb string\n",
                .error = true
            },
            {
                .input = " ",
                .log = "error: [XKB-822] Failed to parse input xkb string\n",
                .error = true
            },
            {
                .input = "\n",
                .log = "error: [XKB-822] Failed to parse input xkb string\n",
                .error = true
            },
            {
                .input = "xkb_keymap {\n",
                .log =
                    "error: [XKB-769] (input string):1:12: syntax error, unexpected end of file\n"
                    "error: [XKB-822] Failed to parse input xkb string\n",
                .error = true
            },
            {
                .input =
                    "xkb_keymap \"\\j\"\n"
                    " { xkb_symbols = {};\n"
                    "};",
                .log =
                    "warning: [XKB-645] (input string):1:12: unknown escape sequence \"\\j\" in string literal\n"
                    "error: [XKB-769] (input string):2:16: syntax error, unexpected =, expecting {\n"
                    "error: [XKB-822] Failed to parse input xkb string\n",
                .error = true
            },
            {
                .input =
                    "xkb_keymap {\n"
                    " xkb_keycodes { <> = 1; };\n"
                    " xkb_symbols { key <> { [\"\xC3\xBC\xff\"] }; };\n"
                    "};",
                .log =
                    "error: [XKB-542] (input string):3:26: Cannot convert string to keysyms: Invalid UTF-8 encoding starting at byte position 3 (code point position: 2).\n"
                    "error: [XKB-822] Failed to parse input xkb string\n",
                .error = true
            },
            /* Extra lines & spaces intented to check the file positions */
            {
                .input =
                    "xkb_keymap {\n"
                    "  xkb_keycodes {\n"
                    "    <> = 1;\n"
                    "\n"
                    "    alias <1> = <>;\n"
                    "    alias <1> =\n"
                    "                <>;\n"
                    "  };\n"
                    "  xkb_types \"\\400x\\j\" { };\n"
                    "  xkb_compat {\n"
                    "    interpret invalidKeysym +\n"
                    "                              Any { repeat = true; };\n"
                    "  };\n"
                    "  xkb_symbols { key <> {[0x30, leftshoe]}; };\n"
                    "};",
                .log =
                    "warning: [XKB-193] (input string):9:13: invalid octal escape sequence \"\\400\" in string literal\n"
                    "warning: [XKB-645] (input string):9:13: unknown escape sequence \"\\j\" in string literal\n"
                    "warning: [XKB-107] (input string):11:15: unrecognized keysym \"invalidKeysym\"\n"
                    "warning: [XKB-489] (input string):14:26: numeric keysym \"0x0030\" (48)\n"
                    "warning: [XKB-301] (input string):14:32: deprecated keysym \"leftshoe\".\n"
                    "warning: [XKB-433] No map in include statement, but \"(input string)\" contains several; Using first defined map, \"(unnamed map)\"\n"
                    "warning: [XKB-523] Alias of <1> for <> declared more than once; First definition ignored\n"
                    "warning: [XKB-286] The type \"TWO_LEVEL\" for key '<>' group 1 was not previously defined; Using the default type\n"
                    "warning: [XKB-516] Type \"default\" has 1 levels, but <> has 2 levels; Ignoring extra symbols\n",
                .error = false
            },
            /* Invalid action fields */
            {
                .input =
                    "default xkb_keymap {\n"
                    "  xkb_compat {\n"
                    "    NoAction.data = 1;\n"
                    "    interpret VoidSymbol { action= VoidAction(data=<garbage>); };\n"
                    "  };\n"
                    "};",
                .log =
                    "error: [XKB-563] The \"NoAction\" action takes no argument, but got \"data\" field; Action definition ignored\n"
                    "error: [XKB-563] The \"VoidAction\" action takes no argument, but got \"data\" field; Action definition ignored\n"
                    "error: Failed to compile xkb_compatibility\n"
                    "error: [XKB-822] Failed to compile keymap\n",
                .error = true
            },
            /* Invalid action fields */
            {
                .input =
                    "default xkb_keymap {\n"
                    "  xkb_keycodes { <> = 1; };\n"
                    "  xkb_symbols {\n"
                    "    NoAction.data = 1;\n"
                    "    key <> { [TerminateServer(data=<garbage>)] };\n"
                    "  };\n"
                    "};",
                .log =
                    "error: [XKB-563] The \"NoAction\" action takes no argument, but got \"data\" field; Action definition ignored\n"
                    "error: [XKB-563] The \"Terminate\" action takes no argument, but got \"data\" field; Action definition ignored\n"
                    "error: [XKB-796] Illegal action definition for <>; Action for group 1/level 1 ignored\n"
                    "error: Failed to compile xkb_symbols\n"
                    "error: [XKB-822] Failed to compile keymap\n",
                .error = true
            },
            /* Unsupported legacy X11 actions */
            {
                .input =
                    "default xkb_keymap {\n"
                    "  xkb_compat {\n"
                    /* This is a valid entry */
                    "    interpret ISO_Lock+AnyOf(all) {\n"
                    "      action= ISOLock(modifiers=modMapMods,affect=all);\n"
                    "    };\n"
                    /* This uses invalid field, but we do not check fields of
                     * legacy actions */
                    "    interpret VoidSymbol+AnyOf(all) {\n"
                    "      action= RedirectKey(data=<garbage>);\n"
                    "    };\n"
                    "  };\n"
                    "};",
                .log =
                    "warning: [XKB-362] Unsupported legacy action type \"ISOLock\".\n"
                    "warning: [XKB-362] Unsupported legacy action type \"RedirectKey\".\n",
                .error = false
            },
            /* Invalid global var */
            {
                .input =
                    "default xkb_keymap {\n"
                    "  xkb_keycodes { foobar; };\n"
                    "};",
                .log =
                    "error: [XKB-639] Default defined for unknown field \"foobar\"; Ignored\n"
                    "error: Failed to compile xkb_keycodes\n"
                    "error: [XKB-822] Failed to compile keymap\n",
                .error = true
            },
            {
                .input =
                    "default xkb_keymap {\n"
                    "  xkb_types { foobar; };\n"
                    "};",
                .log =
                    "error: [XKB-639] Default defined for unknown field \"foobar\"; Ignored\n"
                    "error: Failed to compile xkb_types\n"
                    "error: [XKB-822] Failed to compile keymap\n",
                .error = true
            },
            {
                .input =
                    "default xkb_keymap {\n"
                    "  xkb_compat { foobar; };\n"
                    "};",
                .log =
                    "error: [XKB-639] Default defined for unknown field \"foobar\"; Ignored\n"
                    "error: Failed to compile xkb_compatibility\n"
                    "error: [XKB-822] Failed to compile keymap\n",
                .error = true
            },
            {
                .input =
                    "default xkb_keymap {\n"
                    "  xkb_symbols { foobar; };\n"
                    "};",
                .log =
                    "error: [XKB-639] Default defined for unknown field \"foobar\"; Ignored\n"
                    "error: Failed to compile xkb_symbols\n"
                    "error: [XKB-822] Failed to compile keymap\n",
                .error = true
            },
        };
    
        for (unsigned int k = 0; k < ARRAY_SIZE(keymaps); k++) {
            fprintf(stderr, "------\n*** %s: #%u ***\n", __func__, k);
            struct xkb_keymap *keymap =
                test_compile_buffer(ctx, XKB_KEYMAP_FORMAT_TEXT_V1,
                                    keymaps[k].input, strlen(keymaps[k].input));
            assert_printf(keymaps[k].error ^ !!keymap,
                          "%s\n", darray_items(log_string));
            xkb_keymap_unref(keymap);
            assert_printf(streq_not_null(darray_items(log_string), keymaps[k].log),
                          "Expected:\n%s\nGot:\n%s\n",
                          keymaps[k].log, darray_items(log_string));
            darray_free(log_string);
        }
    
        xkb_context_unref(ctx);
    }
    
    static void
    test_compose(void)
    {
        struct xkb_context *ctx = test_get_context(CONTEXT_NO_FLAG);
        assert(ctx);
    
        darray_char log_string;
        darray_init(log_string);
        xkb_context_set_user_data(ctx, &log_string);
        xkb_context_set_log_fn(ctx, log_fn);
    
        xkb_context_set_log_level(ctx, XKB_LOG_LEVEL_WARNING);
        xkb_context_set_log_verbosity(ctx, 10);
    
        const struct test_data composes[] = {
            {
                .input = "",
                .log = NULL,
                .error = false
            },
            {
                .input = "\n",
                .log = NULL,
                .error = false
            },
            {
                .input = "\xff\n",
                .log =
                    "error: [XKB-542] (input string):1:1: unexpected non-ASCII character.\n"
                    "error: [XKB-542] (input string):1:1: This could be a file encoding issue. Supported file encodings are ASCII and UTF-8.\n"
                    "error: (input string):1:1: failed to parse file\n",
                .error = true
            },
            {
                .input =
                    "<leftshoe> : x\n"
                    "include \"x\"\n",
                .log =
                    "warning: [XKB-301] (input string):1:1: deprecated keysym \"leftshoe\".\n"
                    "error: (input string):2:9: failed to open included Compose file \"x\": No such file or directory\n"
                    "error: (input string):2:9: failed to parse file\n",
                .error = true
            },
            {
                .input =
                    "<a> : \"a\"\n"
                    "\n"
                    "<b> : \"i\\j\\xk\n"
                    "<0x30> : \"\\400\" invalidKeysym\n"
                    "<0> <1> <2> <3> <4> <5> <6> <7> <8> <9> <leftshoe> : \"\"\n",
                .log =
                    "warning: [XKB-645] (input string):3:7: unknown escape sequence \"\\j\" in string literal\n"
                    "warning: [XKB-193] (input string):3:7: illegal hexadecimal escape sequence \"\\x\" in string literal\n"
                    "error: [XKB-685] (input string):3:7: unterminated string literal\n"
                    "warning: [XKB-193] (input string):4:10: illegal octal escape sequence \"\\400\" in string literal\n"
                    "error: (input string):4:17: unrecognized keysym \"invalidKeysym\" on right-hand side\n"
                    "warning: [XKB-301] (input string):5:41: deprecated keysym \"leftshoe\".\n"
                    "warning: [XKB-685] (input string):5:41: too many keysyms (11) on left-hand side; skipping line\n",
                .error = false
            },
            {
                .input =
                    ":\n"
                    "<a> :\n"
                    "#\n"
                    "<c> : \"a\" \"b\"\n"
                    "<d> : a b\n",
                .log =
                    "warning: (input string):1:1: expected at least one keysym on left-hand side; skipping line\n"
                    "warning: [XKB-685] (input string):2:5: right-hand side must have at least one of string or keysym; skipping line\n"
                    "warning: (input string):4:11: right-hand side can have at most one string; skipping line\n"
                    "error: [XKB-685] (input string):5:9: unrecognized modifier \"b\"\n",
                .error = false
            },
            {
                .input =
                    "<a> : a\n"
                    "<a> : a\n"
                    "<b>     : b\n"
                    "<b> <c> : x\n"
                    "<c> <d> : y\n"
                    "<c>     : c\n",
                .log =
                    "warning: (input string):2:7: this compose sequence is a duplicate of another; skipping line\n"
                    "warning: (input string):4:11: a sequence already exists which is a prefix of this sequence; overriding\n"
                    "warning: (input string):6:11: this compose sequence is a prefix of another; overriding\n",
                .error = false
            }
        };
    
        for (unsigned int k = 0; k < ARRAY_SIZE(composes); k++) {
            fprintf(stderr, "------\n*** %s: #%u ***\n", __func__, k);
            struct xkb_compose_table *table = xkb_compose_table_new_from_buffer(
                ctx, composes[k].input, strlen(composes[k].input), "",
                XKB_COMPOSE_FORMAT_TEXT_V1,
                XKB_COMPOSE_COMPILE_NO_FLAGS
            );
            assert(composes[k].error ^ !!table);
            xkb_compose_table_unref(table);
            assert_printf(streq_null(darray_items(log_string), composes[k].log),
                          "Expected:\n%s\nGot:\n%s\n",
                          darray_items(log_string), composes[k].log);
            darray_free(log_string);
        }
    
        xkb_context_unref(ctx);
    }
    
    int
    main(void)
    {
        test_init();
    
        /* We really need to be locale-independent here */
        setlocale(LC_ALL, "C");
    
        test_basic();
        test_keymaps();
        test_compose();
        return EXIT_SUCCESS;
    }