Edit

kc3-lang/libxkbcommon/test/compose.c

Branch :

  • Show log

    Commit

  • Author : Ran Benita
    Date : 2015-03-24 16:40:29
    Hash : 8e1fed6c
    Message : compose: correctly parse modifier syntax As described in: http://cgit.freedesktop.org/xorg/lib/libX11/commit/?id=ddf3b09bb262d01b56fbaade421ac85b0e60a69f Signed-off-by: Ran Benita <ran234@gmail.com>

  • test/compose.c
  • /*
     * Copyright © 2014 Ran Benita <ran234@gmail.com>
     *
     * 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 "xkbcommon/xkbcommon-compose.h"
    
    #include "test.h"
    
    static const char *
    compose_status_string(enum xkb_compose_status status)
    {
        switch (status) {
        case XKB_COMPOSE_NOTHING:
            return "nothing";
        case XKB_COMPOSE_COMPOSING:
            return "composing";
        case XKB_COMPOSE_COMPOSED:
            return "composed";
        case XKB_COMPOSE_CANCELLED:
            return "cancelled";
        }
    
        return "<invalid-status>";
    }
    
    static const char *
    feed_result_string(enum xkb_compose_feed_result result)
    {
        switch (result) {
        case XKB_COMPOSE_FEED_IGNORED:
            return "ignored";
        case XKB_COMPOSE_FEED_ACCEPTED:
            return "accepted";
        }
    
        return "<invalid-result>";
    }
    
    /*
     * Feed a sequence of keysyms to a fresh compose state and test the outcome.
     *
     * The varargs consists of lines in the following format:
     *      <input keysym> <expected feed result> <expected status> <expected string> <expected keysym>
     * Terminated by a line consisting only of XKB_KEY_NoSymbol.
     */
    static bool
    test_compose_seq_va(struct xkb_compose_table *table, va_list ap)
    {
        int ret;
        struct xkb_compose_state *state;
        char buffer[64];
    
        state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
        assert(state);
    
        for (int i = 1; ; i++) {
            xkb_keysym_t input_keysym;
            enum xkb_compose_feed_result result, expected_result;
            enum xkb_compose_status status, expected_status;
            const char *expected_string;
            xkb_keysym_t keysym, expected_keysym;
    
            input_keysym = va_arg(ap, xkb_keysym_t);
            if (input_keysym == XKB_KEY_NoSymbol)
                break;
    
            expected_result = va_arg(ap, enum xkb_compose_feed_result);
            expected_status = va_arg(ap, enum xkb_compose_status);
            expected_string = va_arg(ap, const char *);
            expected_keysym = va_arg(ap, xkb_keysym_t);
    
            result = xkb_compose_state_feed(state, input_keysym);
    
            if (result != expected_result) {
                fprintf(stderr, "after feeding %d keysyms:\n", i);
                fprintf(stderr, "expected feed result: %s\n",
                        feed_result_string(expected_result));
                fprintf(stderr, "got feed result: %s\n",
                        feed_result_string(result));
                goto fail;
            }
    
            status = xkb_compose_state_get_status(state);
            if (status != expected_status) {
                fprintf(stderr, "after feeding %d keysyms:\n", i);
                fprintf(stderr, "expected status: %s\n",
                        compose_status_string(expected_status));
                fprintf(stderr, "got status: %s\n",
                        compose_status_string(status));
                goto fail;
            }
    
            ret = xkb_compose_state_get_utf8(state, buffer, sizeof(buffer));
            if (ret < 0 || (size_t) ret >= sizeof(buffer)) {
                fprintf(stderr, "after feeding %d keysyms:\n", i);
                fprintf(stderr, "expected string: %s\n", expected_string);
                fprintf(stderr, "got error: %d\n", ret);
                goto fail;
            }
            if (!streq(buffer, expected_string)) {
                fprintf(stderr, "after feeding %d keysyms:\n", i);
                fprintf(stderr, "expected string: %s\n", strempty(expected_string));
                fprintf(stderr, "got string: %s\n", buffer);
                goto fail;
            }
    
            keysym = xkb_compose_state_get_one_sym(state);
            if (keysym != expected_keysym) {
                fprintf(stderr, "after feeding %d keysyms:\n", i);
                xkb_keysym_get_name(expected_keysym, buffer, sizeof(buffer));
                fprintf(stderr, "expected keysym: %s\n", buffer);
                xkb_keysym_get_name(keysym, buffer, sizeof(buffer));
                fprintf(stderr, "got keysym (%#x): %s\n", keysym, buffer);
                goto fail;
            }
        }
    
        xkb_compose_state_unref(state);
        return true;
    
    fail:
        xkb_compose_state_unref(state);
        return false;
    }
    
    static bool
    test_compose_seq(struct xkb_compose_table *table, ...)
    {
        va_list ap;
        bool ok;
        va_start(ap, table);
        ok = test_compose_seq_va(table, ap);
        va_end(ap);
        return ok;
    }
    
    static bool
    test_compose_seq_buffer(struct xkb_context *ctx, const char *buffer, ...)
    {
        va_list ap;
        bool ok;
        struct xkb_compose_table *table;
        table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
                                                  XKB_COMPOSE_FORMAT_TEXT_V1,
                                                  XKB_COMPOSE_COMPILE_NO_FLAGS);
        assert(table);
        va_start(ap, buffer);
        ok = test_compose_seq_va(table, ap);
        va_end(ap);
        xkb_compose_table_unref(table);
        return ok;
    }
    
    static void
    test_seqs(struct xkb_context *ctx)
    {
        struct xkb_compose_table *table;
        char *path;
        FILE *file;
    
        path = test_get_path("compose/en_US.UTF-8/Compose");
        file = fopen(path, "r");
        assert(file);
        free(path);
    
        table = xkb_compose_table_new_from_file(ctx, file, "",
                                                XKB_COMPOSE_FORMAT_TEXT_V1,
                                                XKB_COMPOSE_COMPILE_NO_FLAGS);
        assert(table);
        fclose(file);
    
        assert(test_compose_seq(table,
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
            XKB_KEY_NoSymbol));
    
        assert(test_compose_seq(table,
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
            XKB_KEY_NoSymbol));
    
        assert(test_compose_seq(table,
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
            XKB_KEY_NoSymbol));
    
        assert(test_compose_seq(table,
            XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "'",    XKB_KEY_apostrophe,
            XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSED,   "'",    XKB_KEY_apostrophe,
            XKB_KEY_NoSymbol));
    
        assert(test_compose_seq(table,
            XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "´",    XKB_KEY_acute,
            XKB_KEY_NoSymbol));
    
        assert(test_compose_seq(table,
            XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_Shift_L,        XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_Control_L,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_T,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "@",    XKB_KEY_at,
            XKB_KEY_NoSymbol));
    
        assert(test_compose_seq(table,
            XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
            XKB_KEY_a,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
            XKB_KEY_b,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
            XKB_KEY_NoSymbol));
    
        assert(test_compose_seq(table,
            XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_apostrophe,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
            XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
            XKB_KEY_NoSymbol));
    
        xkb_compose_table_unref(table);
    
        /* Make sure one-keysym sequences work. */
        assert(test_compose_seq_buffer(ctx,
            "<A>          :  \"foo\"  X \n"
            "<B> <A>      :  \"baz\"  Y \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "foo",   XKB_KEY_X,
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "foo",   XKB_KEY_X,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "baz",   XKB_KEY_Y,
            XKB_KEY_NoSymbol));
    
        /* No sequences at all. */
        assert(test_compose_seq_buffer(ctx,
            "",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
            XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
            XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
            XKB_KEY_NoSymbol));
    
        /* Only keysym - string derived from keysym. */
        assert(test_compose_seq_buffer(ctx,
            "<A> <B>     :  X \n"
            "<B> <A>     :  dollar \n"
            "<C>         :  dead_acute \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "X",     XKB_KEY_X,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "$",     XKB_KEY_dollar,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "",      XKB_KEY_dead_acute,
            XKB_KEY_NoSymbol));
    
        /* Make sure a cancelling keysym doesn't start a new sequence. */
        assert(test_compose_seq_buffer(ctx,
            "<A> <B>     :  X \n"
            "<C> <D>     :  Y \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED, "",      XKB_KEY_NoSymbol,
            XKB_KEY_D,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED, "",      XKB_KEY_NoSymbol,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
            XKB_KEY_D,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "Y",     XKB_KEY_Y,
            XKB_KEY_NoSymbol));
    }
    
    static void
    test_conflicting(struct xkb_context *ctx)
    {
        // new is prefix of old
        assert(test_compose_seq_buffer(ctx,
            "<A> <B> <C>  :  \"foo\"  A \n"
            "<A> <B>      :  \"bar\"  B \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_A,
            XKB_KEY_NoSymbol));
    
        // old is a prefix of new
        assert(test_compose_seq_buffer(ctx,
            "<A> <B>      :  \"bar\"  B \n"
            "<A> <B> <C>  :  \"foo\"  A \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_A,
            XKB_KEY_NoSymbol));
    
        // new duplicate of old
        assert(test_compose_seq_buffer(ctx,
            "<A> <B>      :  \"bar\"  B \n"
            "<A> <B>      :  \"bar\"  B \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_B,
            XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
            XKB_KEY_NoSymbol));
    
        // new same length as old #1
        assert(test_compose_seq_buffer(ctx,
            "<A> <B>      :  \"foo\"  A \n"
            "<A> <B>      :  \"bar\"  B \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_B,
            XKB_KEY_NoSymbol));
    
        // new same length as old #2
        assert(test_compose_seq_buffer(ctx,
            "<A> <B>      :  \"foo\"  A \n"
            "<A> <B>      :  \"foo\"  B \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_B,
            XKB_KEY_NoSymbol));
    
        // new same length as old #3
        assert(test_compose_seq_buffer(ctx,
            "<A> <B>      :  \"foo\"  A \n"
            "<A> <B>      :  \"bar\"  A \n",
            XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_A,
            XKB_KEY_NoSymbol));
    }
    
    static void
    test_state(struct xkb_context *ctx)
    {
        struct xkb_compose_table *table;
        struct xkb_compose_state *state;
        char *path;
        FILE *file;
    
        path = test_get_path("compose/en_US.UTF-8/Compose");
        file = fopen(path, "r");
        assert(file);
        free(path);
    
        table = xkb_compose_table_new_from_file(ctx, file, "",
                                                XKB_COMPOSE_FORMAT_TEXT_V1,
                                                XKB_COMPOSE_COMPILE_NO_FLAGS);
        assert(table);
        fclose(file);
    
        state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
        assert(state);
    
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
        xkb_compose_state_reset(state);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
        xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
        xkb_compose_state_feed(state, XKB_KEY_Multi_key);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
        xkb_compose_state_reset(state);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
        xkb_compose_state_feed(state, XKB_KEY_Multi_key);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
        xkb_compose_state_feed(state, XKB_KEY_Multi_key);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
        xkb_compose_state_feed(state, XKB_KEY_Multi_key);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
        xkb_compose_state_feed(state, XKB_KEY_Multi_key);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
        xkb_compose_state_reset(state);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
        xkb_compose_state_feed(state, XKB_KEY_dead_acute);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
        xkb_compose_state_feed(state, XKB_KEY_A);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
        xkb_compose_state_reset(state);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
        xkb_compose_state_feed(state, XKB_KEY_dead_acute);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
        xkb_compose_state_feed(state, XKB_KEY_A);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
        xkb_compose_state_reset(state);
        xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
        assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    
        xkb_compose_state_unref(state);
        xkb_compose_table_unref(table);
    }
    
    static void
    test_XCOMPOSEFILE(struct xkb_context *ctx)
    {
        struct xkb_compose_table *table;
        char *path;
    
        path = test_get_path("compose/en_US.UTF-8/Compose");
        setenv("XCOMPOSEFILE", path, 1);
        free(path);
    
        table = xkb_compose_table_new_from_locale(ctx, "blabla",
                                                  XKB_COMPOSE_COMPILE_NO_FLAGS);
        assert(table);
    
        assert(test_compose_seq(table,
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
            XKB_KEY_NoSymbol));
    
        xkb_compose_table_unref(table);
    }
    
    static void
    test_modifier_syntax(struct xkb_context *ctx)
    {
        const char *table_string;
    
        /* We don't do anything with the modifiers, but make sure we can parse
         * them. */
    
        assert(test_compose_seq_buffer(ctx,
            "None <A>          : X \n"
            "Shift <B>         : Y \n"
            "Ctrl <C>          : Y \n"
            "Alt <D>           : Y \n"
            "Caps <E>          : Y \n"
            "Lock <F>          : Y \n"
            "Shift Ctrl <G>    : Y \n"
            "~Shift <H>        : Y \n"
            "~Shift Ctrl <I>   : Y \n"
            "Shift ~Ctrl <J>   : Y \n"
            "Shift ~Ctrl ~Alt <K> : Y \n"
            "! Shift <B>       : Y \n"
            "! Ctrl <C>        : Y \n"
            "! Alt <D>         : Y \n"
            "! Caps <E>        : Y \n"
            "! Lock <F>        : Y \n"
            "! Shift Ctrl <G>  : Y \n"
            "! ~Shift <H>      : Y \n"
            "! ~Shift Ctrl <I> : Y \n"
            "! Shift ~Ctrl <J> : Y \n"
            "! Shift ~Ctrl ~Alt <K> : Y \n"
            "<L> ! Shift <M>   : Y \n"
            "None <N> ! Shift <O> : Y \n"
            "None <P> ! Shift <Q> : Y \n",
            XKB_KEY_NoSymbol));
    
        fprintf(stderr, "<START bad input string>\n");
        table_string =
            "! None <A>        : X \n"
            "! Foo <B>         : X \n"
            "None ! Shift <C>  : X \n"
            "! ! <D>           : X \n"
            "! ~ <E>           : X \n"
            "! ! <F>           : X \n"
            "! Ctrl ! Ctrl <G> : X \n"
            "<H> !             : X \n"
            "<I> None          : X \n"
            "None None <J>     : X \n"
            "<K>               : !Shift X \n";
        assert(!xkb_compose_table_new_from_buffer(ctx, table_string,
                                                  strlen(table_string), "C",
                                                  XKB_COMPOSE_FORMAT_TEXT_V1,
                                                  XKB_COMPOSE_COMPILE_NO_FLAGS));
        fprintf(stderr, "<END bad input string>\n");
    }
    
    static void
    test_include(struct xkb_context *ctx)
    {
        char *path, *table_string;
        int ret;
    
        path = test_get_path("compose/en_US.UTF-8/Compose");
        assert(path);
    
        /* We don't have a mechanism to change the include paths like we
         * have for keymaps. So we must include the full path. */
        ret = asprintf(&table_string,
            "<dead_tilde> <space>   : \"foo\" X\n"
            "include \"%s\"\n"
            "<dead_tilde> <dead_tilde> : \"bar\" Y\n", path);
        assert(ret >= 0);
    
        assert(test_compose_seq_buffer(ctx, table_string,
            /* No conflict. */
            XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "´",    XKB_KEY_acute,
    
            /* Comes before - doesn't override. */
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
    
            /* Comes after - does override. */
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
            XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_Y,
    
            XKB_KEY_NoSymbol));
    
        free(path);
        free(table_string);
    }
    
    int
    main(int argc, char *argv[])
    {
        struct xkb_context *ctx;
    
        ctx = test_get_context(CONTEXT_NO_FLAG);
        assert(ctx);
    
        test_seqs(ctx);
        test_conflicting(ctx);
        test_XCOMPOSEFILE(ctx);
        test_state(ctx);
        test_modifier_syntax(ctx);
        test_include(ctx);
    
        xkb_context_unref(ctx);
        return 0;
    }