Home | History | Annotate | Download | only in test
      1 /*
      2  * Copyright  2014 Ran Benita <ran234 (at) gmail.com>
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     21  * DEALINGS IN THE SOFTWARE.
     22  */
     23 
     24 #include "xkbcommon/xkbcommon-compose.h"
     25 
     26 #include "test.h"
     27 
     28 static const char *
     29 compose_status_string(enum xkb_compose_status status)
     30 {
     31     switch (status) {
     32     case XKB_COMPOSE_NOTHING:
     33         return "nothing";
     34     case XKB_COMPOSE_COMPOSING:
     35         return "composing";
     36     case XKB_COMPOSE_COMPOSED:
     37         return "composed";
     38     case XKB_COMPOSE_CANCELLED:
     39         return "cancelled";
     40     }
     41 
     42     return "<invalid-status>";
     43 }
     44 
     45 static const char *
     46 feed_result_string(enum xkb_compose_feed_result result)
     47 {
     48     switch (result) {
     49     case XKB_COMPOSE_FEED_IGNORED:
     50         return "ignored";
     51     case XKB_COMPOSE_FEED_ACCEPTED:
     52         return "accepted";
     53     }
     54 
     55     return "<invalid-result>";
     56 }
     57 
     58 /*
     59  * Feed a sequence of keysyms to a fresh compose state and test the outcome.
     60  *
     61  * The varargs consists of lines in the following format:
     62  *      <input keysym> <expected feed result> <expected status> <expected string> <expected keysym>
     63  * Terminated by a line consisting only of XKB_KEY_NoSymbol.
     64  */
     65 static bool
     66 test_compose_seq_va(struct xkb_compose_table *table, va_list ap)
     67 {
     68     int ret;
     69     struct xkb_compose_state *state;
     70     char buffer[64];
     71 
     72     state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
     73     assert(state);
     74 
     75     for (int i = 1; ; i++) {
     76         xkb_keysym_t input_keysym;
     77         enum xkb_compose_feed_result result, expected_result;
     78         enum xkb_compose_status status, expected_status;
     79         const char *expected_string;
     80         xkb_keysym_t keysym, expected_keysym;
     81 
     82         input_keysym = va_arg(ap, xkb_keysym_t);
     83         if (input_keysym == XKB_KEY_NoSymbol)
     84             break;
     85 
     86         expected_result = va_arg(ap, enum xkb_compose_feed_result);
     87         expected_status = va_arg(ap, enum xkb_compose_status);
     88         expected_string = va_arg(ap, const char *);
     89         expected_keysym = va_arg(ap, xkb_keysym_t);
     90 
     91         result = xkb_compose_state_feed(state, input_keysym);
     92 
     93         if (result != expected_result) {
     94             fprintf(stderr, "after feeding %d keysyms:\n", i);
     95             fprintf(stderr, "expected feed result: %s\n",
     96                     feed_result_string(expected_result));
     97             fprintf(stderr, "got feed result: %s\n",
     98                     feed_result_string(result));
     99             goto fail;
    100         }
    101 
    102         status = xkb_compose_state_get_status(state);
    103         if (status != expected_status) {
    104             fprintf(stderr, "after feeding %d keysyms:\n", i);
    105             fprintf(stderr, "expected status: %s\n",
    106                     compose_status_string(expected_status));
    107             fprintf(stderr, "got status: %s\n",
    108                     compose_status_string(status));
    109             goto fail;
    110         }
    111 
    112         ret = xkb_compose_state_get_utf8(state, buffer, sizeof(buffer));
    113         if (ret < 0 || (size_t) ret >= sizeof(buffer)) {
    114             fprintf(stderr, "after feeding %d keysyms:\n", i);
    115             fprintf(stderr, "expected string: %s\n", expected_string);
    116             fprintf(stderr, "got error: %d\n", ret);
    117             goto fail;
    118         }
    119         if (!streq(buffer, expected_string)) {
    120             fprintf(stderr, "after feeding %d keysyms:\n", i);
    121             fprintf(stderr, "expected string: %s\n", strempty(expected_string));
    122             fprintf(stderr, "got string: %s\n", buffer);
    123             goto fail;
    124         }
    125 
    126         keysym = xkb_compose_state_get_one_sym(state);
    127         if (keysym != expected_keysym) {
    128             fprintf(stderr, "after feeding %d keysyms:\n", i);
    129             xkb_keysym_get_name(expected_keysym, buffer, sizeof(buffer));
    130             fprintf(stderr, "expected keysym: %s\n", buffer);
    131             xkb_keysym_get_name(keysym, buffer, sizeof(buffer));
    132             fprintf(stderr, "got keysym (%#x): %s\n", keysym, buffer);
    133             goto fail;
    134         }
    135     }
    136 
    137     xkb_compose_state_unref(state);
    138     return true;
    139 
    140 fail:
    141     xkb_compose_state_unref(state);
    142     return false;
    143 }
    144 
    145 static bool
    146 test_compose_seq(struct xkb_compose_table *table, ...)
    147 {
    148     va_list ap;
    149     bool ok;
    150     va_start(ap, table);
    151     ok = test_compose_seq_va(table, ap);
    152     va_end(ap);
    153     return ok;
    154 }
    155 
    156 static bool
    157 test_compose_seq_buffer(struct xkb_context *ctx, const char *buffer, ...)
    158 {
    159     va_list ap;
    160     bool ok;
    161     struct xkb_compose_table *table;
    162     table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
    163                                               XKB_COMPOSE_FORMAT_TEXT_V1,
    164                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
    165     assert(table);
    166     va_start(ap, buffer);
    167     ok = test_compose_seq_va(table, ap);
    168     va_end(ap);
    169     xkb_compose_table_unref(table);
    170     return ok;
    171 }
    172 
    173 static void
    174 test_seqs(struct xkb_context *ctx)
    175 {
    176     struct xkb_compose_table *table;
    177     char *path;
    178     FILE *file;
    179 
    180     path = test_get_path("compose/en_US.UTF-8/Compose");
    181     file = fopen(path, "r");
    182     assert(file);
    183     free(path);
    184 
    185     table = xkb_compose_table_new_from_file(ctx, file, "",
    186                                             XKB_COMPOSE_FORMAT_TEXT_V1,
    187                                             XKB_COMPOSE_COMPILE_NO_FLAGS);
    188     assert(table);
    189     fclose(file);
    190 
    191     assert(test_compose_seq(table,
    192         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    193         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
    194         XKB_KEY_NoSymbol));
    195 
    196     assert(test_compose_seq(table,
    197         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    198         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
    199         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    200         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
    201         XKB_KEY_NoSymbol));
    202 
    203     assert(test_compose_seq(table,
    204         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    205         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
    206         XKB_KEY_NoSymbol));
    207 
    208     assert(test_compose_seq(table,
    209         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    210         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "'",    XKB_KEY_apostrophe,
    211         XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSED,   "'",    XKB_KEY_apostrophe,
    212         XKB_KEY_NoSymbol));
    213 
    214     assert(test_compose_seq(table,
    215         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    216         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "",    XKB_KEY_acute,
    217         XKB_KEY_NoSymbol));
    218 
    219     assert(test_compose_seq(table,
    220         XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    221         XKB_KEY_Shift_L,        XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    222         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    223         XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    224         XKB_KEY_Control_L,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    225         XKB_KEY_T,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "@",    XKB_KEY_at,
    226         XKB_KEY_NoSymbol));
    227 
    228     assert(test_compose_seq(table,
    229         XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
    230         XKB_KEY_a,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
    231         XKB_KEY_b,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
    232         XKB_KEY_NoSymbol));
    233 
    234     assert(test_compose_seq(table,
    235         XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    236         XKB_KEY_apostrophe,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    237         XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED,  "",     XKB_KEY_NoSymbol,
    238         XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
    239         XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
    240         XKB_KEY_NoSymbol));
    241 
    242     xkb_compose_table_unref(table);
    243 
    244     /* Make sure one-keysym sequences work. */
    245     assert(test_compose_seq_buffer(ctx,
    246         "<A>          :  \"foo\"  X \n"
    247         "<B> <A>      :  \"baz\"  Y \n",
    248         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "foo",   XKB_KEY_X,
    249         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "foo",   XKB_KEY_X,
    250         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
    251         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
    252         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "baz",   XKB_KEY_Y,
    253         XKB_KEY_NoSymbol));
    254 
    255     /* No sequences at all. */
    256     assert(test_compose_seq_buffer(ctx,
    257         "",
    258         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
    259         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
    260         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
    261         XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
    262         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
    263         XKB_KEY_NoSymbol));
    264 
    265     /* Only keysym - string derived from keysym. */
    266     assert(test_compose_seq_buffer(ctx,
    267         "<A> <B>     :  X \n"
    268         "<B> <A>     :  dollar \n"
    269         "<C>         :  dead_acute \n",
    270         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
    271         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "X",     XKB_KEY_X,
    272         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
    273         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "$",     XKB_KEY_dollar,
    274         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "",      XKB_KEY_dead_acute,
    275         XKB_KEY_NoSymbol));
    276 
    277     /* Make sure a cancelling keysym doesn't start a new sequence. */
    278     assert(test_compose_seq_buffer(ctx,
    279         "<A> <B>     :  X \n"
    280         "<C> <D>     :  Y \n",
    281         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
    282         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED, "",      XKB_KEY_NoSymbol,
    283         XKB_KEY_D,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
    284         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
    285         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED, "",      XKB_KEY_NoSymbol,
    286         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
    287         XKB_KEY_D,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "Y",     XKB_KEY_Y,
    288         XKB_KEY_NoSymbol));
    289 }
    290 
    291 static void
    292 test_conflicting(struct xkb_context *ctx)
    293 {
    294     // new is prefix of old
    295     assert(test_compose_seq_buffer(ctx,
    296         "<A> <B> <C>  :  \"foo\"  A \n"
    297         "<A> <B>      :  \"bar\"  B \n",
    298         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    299         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    300         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_A,
    301         XKB_KEY_NoSymbol));
    302 
    303     // old is a prefix of new
    304     assert(test_compose_seq_buffer(ctx,
    305         "<A> <B>      :  \"bar\"  B \n"
    306         "<A> <B> <C>  :  \"foo\"  A \n",
    307         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    308         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    309         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_A,
    310         XKB_KEY_NoSymbol));
    311 
    312     // new duplicate of old
    313     assert(test_compose_seq_buffer(ctx,
    314         "<A> <B>      :  \"bar\"  B \n"
    315         "<A> <B>      :  \"bar\"  B \n",
    316         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    317         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_B,
    318         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
    319         XKB_KEY_NoSymbol));
    320 
    321     // new same length as old #1
    322     assert(test_compose_seq_buffer(ctx,
    323         "<A> <B>      :  \"foo\"  A \n"
    324         "<A> <B>      :  \"bar\"  B \n",
    325         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    326         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_B,
    327         XKB_KEY_NoSymbol));
    328 
    329     // new same length as old #2
    330     assert(test_compose_seq_buffer(ctx,
    331         "<A> <B>      :  \"foo\"  A \n"
    332         "<A> <B>      :  \"foo\"  B \n",
    333         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    334         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_B,
    335         XKB_KEY_NoSymbol));
    336 
    337     // new same length as old #3
    338     assert(test_compose_seq_buffer(ctx,
    339         "<A> <B>      :  \"foo\"  A \n"
    340         "<A> <B>      :  \"bar\"  A \n",
    341         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    342         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_A,
    343         XKB_KEY_NoSymbol));
    344 }
    345 
    346 static void
    347 test_state(struct xkb_context *ctx)
    348 {
    349     struct xkb_compose_table *table;
    350     struct xkb_compose_state *state;
    351     char *path;
    352     FILE *file;
    353 
    354     path = test_get_path("compose/en_US.UTF-8/Compose");
    355     file = fopen(path, "r");
    356     assert(file);
    357     free(path);
    358 
    359     table = xkb_compose_table_new_from_file(ctx, file, "",
    360                                             XKB_COMPOSE_FORMAT_TEXT_V1,
    361                                             XKB_COMPOSE_COMPILE_NO_FLAGS);
    362     assert(table);
    363     fclose(file);
    364 
    365     state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
    366     assert(state);
    367 
    368     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    369     xkb_compose_state_reset(state);
    370     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    371     xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
    372     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    373     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
    374     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
    375     xkb_compose_state_reset(state);
    376     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    377     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
    378     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
    379     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
    380     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
    381     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
    382     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
    383     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
    384     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
    385     xkb_compose_state_reset(state);
    386     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    387     xkb_compose_state_feed(state, XKB_KEY_dead_acute);
    388     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
    389     xkb_compose_state_feed(state, XKB_KEY_A);
    390     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
    391     xkb_compose_state_reset(state);
    392     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    393     xkb_compose_state_feed(state, XKB_KEY_dead_acute);
    394     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
    395     xkb_compose_state_feed(state, XKB_KEY_A);
    396     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
    397     xkb_compose_state_reset(state);
    398     xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
    399     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
    400 
    401     xkb_compose_state_unref(state);
    402     xkb_compose_table_unref(table);
    403 }
    404 
    405 static void
    406 test_XCOMPOSEFILE(struct xkb_context *ctx)
    407 {
    408     struct xkb_compose_table *table;
    409     char *path;
    410 
    411     path = test_get_path("compose/en_US.UTF-8/Compose");
    412     setenv("XCOMPOSEFILE", path, 1);
    413     free(path);
    414 
    415     table = xkb_compose_table_new_from_locale(ctx, "blabla",
    416                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
    417     assert(table);
    418 
    419     assert(test_compose_seq(table,
    420         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    421         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
    422         XKB_KEY_NoSymbol));
    423 
    424     xkb_compose_table_unref(table);
    425 }
    426 
    427 static void
    428 test_modifier_syntax(struct xkb_context *ctx)
    429 {
    430     const char *table_string;
    431 
    432     /* We don't do anything with the modifiers, but make sure we can parse
    433      * them. */
    434 
    435     assert(test_compose_seq_buffer(ctx,
    436         "None <A>          : X \n"
    437         "! Shift <B>       : Y \n"
    438         "! Ctrl <C>        : Y \n"
    439         "! Alt <D>         : Y \n"
    440         "! Caps <E>        : Y \n"
    441         "! Lock <F>        : Y \n"
    442         "! Shift Ctrl <G>  : Y \n"
    443         "! ~Shift <H>      : Y \n"
    444         "! ~Shift Ctrl <I> : Y \n"
    445         "! Shift ~Ctrl <J> : Y \n"
    446         "! Shift ~Ctrl ~Alt <K> : Y \n"
    447         "<L> ! Shift <M>   : Y \n"
    448         "None <N> ! Shift <O> : Y \n"
    449         "None <P> ! Shift <Q> : Y \n",
    450         XKB_KEY_NoSymbol));
    451 
    452     fprintf(stderr, "<START bad input string>\n");
    453     table_string =
    454         "! None <A>        : X \n"
    455         "! Foo <B>         : X \n"
    456         "None ! Shift <C>  : X \n"
    457         "! <D>             : X \n"
    458         "! ~ <E>           : X \n"
    459         "! ! <F>           : X \n"
    460         "! Ctrl ! Ctrl <G> : X \n"
    461         "<H> !             : X \n"
    462         "<I> None          : X \n"
    463         "None None <J>     : X \n"
    464         "<K>               : !Shift X \n";
    465     assert(!xkb_compose_table_new_from_buffer(ctx, table_string,
    466                                               strlen(table_string), "C",
    467                                               XKB_COMPOSE_FORMAT_TEXT_V1,
    468                                               XKB_COMPOSE_COMPILE_NO_FLAGS));
    469     fprintf(stderr, "<END bad input string>\n");
    470 }
    471 
    472 static void
    473 test_include(struct xkb_context *ctx)
    474 {
    475     char *path, *table_string;
    476     int ret;
    477 
    478     path = test_get_path("compose/en_US.UTF-8/Compose");
    479     assert(path);
    480 
    481     /* We don't have a mechanism to change the include paths like we
    482      * have for keymaps. So we must include the full path. */
    483     ret = asprintf(&table_string,
    484         "<dead_tilde> <space>   : \"foo\" X\n"
    485         "include \"%s\"\n"
    486         "<dead_tilde> <dead_tilde> : \"bar\" Y\n", path);
    487     assert(ret >= 0);
    488 
    489     assert(test_compose_seq_buffer(ctx, table_string,
    490         /* No conflict. */
    491         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    492         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "",    XKB_KEY_acute,
    493 
    494         /* Comes before - doesn't override. */
    495         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    496         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
    497 
    498         /* Comes after - does override. */
    499         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
    500         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_Y,
    501 
    502         XKB_KEY_NoSymbol));
    503 
    504     free(path);
    505     free(table_string);
    506 }
    507 
    508 int
    509 main(int argc, char *argv[])
    510 {
    511     struct xkb_context *ctx;
    512 
    513     ctx = test_get_context(CONTEXT_NO_FLAG);
    514     assert(ctx);
    515 
    516     test_seqs(ctx);
    517     test_conflicting(ctx);
    518     test_XCOMPOSEFILE(ctx);
    519     test_state(ctx);
    520     test_modifier_syntax(ctx);
    521     test_include(ctx);
    522 
    523     xkb_context_unref(ctx);
    524     return 0;
    525 }
    526