compose: correctly parse modifier syntax
[platform/upstream/libxkbcommon.git] / test / compose.c
1 /*
2  * Copyright © 2014 Ran Benita <ran234@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         "! Shift <B>       : Y \n"
448         "! Ctrl <C>        : Y \n"
449         "! Alt <D>         : Y \n"
450         "! Caps <E>        : Y \n"
451         "! Lock <F>        : Y \n"
452         "! Shift Ctrl <G>  : Y \n"
453         "! ~Shift <H>      : Y \n"
454         "! ~Shift Ctrl <I> : Y \n"
455         "! Shift ~Ctrl <J> : Y \n"
456         "! Shift ~Ctrl ~Alt <K> : Y \n"
457         "<L> ! Shift <M>   : Y \n"
458         "None <N> ! Shift <O> : Y \n"
459         "None <P> ! Shift <Q> : Y \n",
460         XKB_KEY_NoSymbol));
461
462     fprintf(stderr, "<START bad input string>\n");
463     table_string =
464         "! None <A>        : X \n"
465         "! Foo <B>         : X \n"
466         "None ! Shift <C>  : X \n"
467         "! ! <D>           : X \n"
468         "! ~ <E>           : X \n"
469         "! ! <F>           : X \n"
470         "! Ctrl ! Ctrl <G> : X \n"
471         "<H> !             : X \n"
472         "<I> None          : X \n"
473         "None None <J>     : X \n"
474         "<K>               : !Shift X \n";
475     assert(!xkb_compose_table_new_from_buffer(ctx, table_string,
476                                               strlen(table_string), "C",
477                                               XKB_COMPOSE_FORMAT_TEXT_V1,
478                                               XKB_COMPOSE_COMPILE_NO_FLAGS));
479     fprintf(stderr, "<END bad input string>\n");
480 }
481
482 static void
483 test_include(struct xkb_context *ctx)
484 {
485     char *path, *table_string;
486     int ret;
487
488     path = test_get_path("compose/en_US.UTF-8/Compose");
489     assert(path);
490
491     /* We don't have a mechanism to change the include paths like we
492      * have for keymaps. So we must include the full path. */
493     ret = asprintf(&table_string,
494         "<dead_tilde> <space>   : \"foo\" X\n"
495         "include \"%s\"\n"
496         "<dead_tilde> <dead_tilde> : \"bar\" Y\n", path);
497     assert(ret >= 0);
498
499     assert(test_compose_seq_buffer(ctx, table_string,
500         /* No conflict. */
501         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
502         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "´",    XKB_KEY_acute,
503
504         /* Comes before - doesn't override. */
505         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
506         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
507
508         /* Comes after - does override. */
509         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
510         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_Y,
511
512         XKB_KEY_NoSymbol));
513
514     free(path);
515     free(table_string);
516 }
517
518 int
519 main(int argc, char *argv[])
520 {
521     struct xkb_context *ctx;
522
523     ctx = test_get_context(CONTEXT_NO_FLAG);
524     assert(ctx);
525
526     test_seqs(ctx);
527     test_conflicting(ctx);
528     test_XCOMPOSEFILE(ctx);
529     test_state(ctx);
530     test_modifier_syntax(ctx);
531     test_include(ctx);
532
533     xkb_context_unref(ctx);
534     return 0;
535 }