5e7cba0f3f58f3c771cf95942d518547f0a6b91d
[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 "config.h"
25
26 #include "xkbcommon/xkbcommon-compose.h"
27
28 #include "test.h"
29
30 static const char *
31 compose_status_string(enum xkb_compose_status status)
32 {
33     switch (status) {
34     case XKB_COMPOSE_NOTHING:
35         return "nothing";
36     case XKB_COMPOSE_COMPOSING:
37         return "composing";
38     case XKB_COMPOSE_COMPOSED:
39         return "composed";
40     case XKB_COMPOSE_CANCELLED:
41         return "cancelled";
42     }
43
44     return "<invalid-status>";
45 }
46
47 static const char *
48 feed_result_string(enum xkb_compose_feed_result result)
49 {
50     switch (result) {
51     case XKB_COMPOSE_FEED_IGNORED:
52         return "ignored";
53     case XKB_COMPOSE_FEED_ACCEPTED:
54         return "accepted";
55     }
56
57     return "<invalid-result>";
58 }
59
60 /*
61  * Feed a sequence of keysyms to a fresh compose state and test the outcome.
62  *
63  * The varargs consists of lines in the following format:
64  *      <input keysym> <expected feed result> <expected status> <expected string> <expected keysym>
65  * Terminated by a line consisting only of XKB_KEY_NoSymbol.
66  */
67 static bool
68 test_compose_seq_va(struct xkb_compose_table *table, va_list ap)
69 {
70     int ret;
71     struct xkb_compose_state *state;
72     char buffer[64];
73
74     state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
75     assert(state);
76
77     for (int i = 1; ; i++) {
78         xkb_keysym_t input_keysym;
79         enum xkb_compose_feed_result result, expected_result;
80         enum xkb_compose_status status, expected_status;
81         const char *expected_string;
82         xkb_keysym_t keysym, expected_keysym;
83
84         input_keysym = va_arg(ap, xkb_keysym_t);
85         if (input_keysym == XKB_KEY_NoSymbol)
86             break;
87
88         expected_result = va_arg(ap, enum xkb_compose_feed_result);
89         expected_status = va_arg(ap, enum xkb_compose_status);
90         expected_string = va_arg(ap, const char *);
91         expected_keysym = va_arg(ap, xkb_keysym_t);
92
93         result = xkb_compose_state_feed(state, input_keysym);
94
95         if (result != expected_result) {
96             fprintf(stderr, "after feeding %d keysyms:\n", i);
97             fprintf(stderr, "expected feed result: %s\n",
98                     feed_result_string(expected_result));
99             fprintf(stderr, "got feed result: %s\n",
100                     feed_result_string(result));
101             goto fail;
102         }
103
104         status = xkb_compose_state_get_status(state);
105         if (status != expected_status) {
106             fprintf(stderr, "after feeding %d keysyms:\n", i);
107             fprintf(stderr, "expected status: %s\n",
108                     compose_status_string(expected_status));
109             fprintf(stderr, "got status: %s\n",
110                     compose_status_string(status));
111             goto fail;
112         }
113
114         ret = xkb_compose_state_get_utf8(state, buffer, sizeof(buffer));
115         if (ret < 0 || (size_t) ret >= sizeof(buffer)) {
116             fprintf(stderr, "after feeding %d keysyms:\n", i);
117             fprintf(stderr, "expected string: %s\n", expected_string);
118             fprintf(stderr, "got error: %d\n", ret);
119             goto fail;
120         }
121         if (!streq(buffer, expected_string)) {
122             fprintf(stderr, "after feeding %d keysyms:\n", i);
123             fprintf(stderr, "expected string: %s\n", strempty(expected_string));
124             fprintf(stderr, "got string: %s\n", buffer);
125             goto fail;
126         }
127
128         keysym = xkb_compose_state_get_one_sym(state);
129         if (keysym != expected_keysym) {
130             fprintf(stderr, "after feeding %d keysyms:\n", i);
131             xkb_keysym_get_name(expected_keysym, buffer, sizeof(buffer));
132             fprintf(stderr, "expected keysym: %s\n", buffer);
133             xkb_keysym_get_name(keysym, buffer, sizeof(buffer));
134             fprintf(stderr, "got keysym (%#x): %s\n", keysym, buffer);
135             goto fail;
136         }
137     }
138
139     xkb_compose_state_unref(state);
140     return true;
141
142 fail:
143     xkb_compose_state_unref(state);
144     return false;
145 }
146
147 static bool
148 test_compose_seq(struct xkb_compose_table *table, ...)
149 {
150     va_list ap;
151     bool ok;
152     va_start(ap, table);
153     ok = test_compose_seq_va(table, ap);
154     va_end(ap);
155     return ok;
156 }
157
158 static bool
159 test_compose_seq_buffer(struct xkb_context *ctx, const char *buffer, ...)
160 {
161     va_list ap;
162     bool ok;
163     struct xkb_compose_table *table;
164     table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
165                                               XKB_COMPOSE_FORMAT_TEXT_V1,
166                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
167     assert(table);
168     va_start(ap, buffer);
169     ok = test_compose_seq_va(table, ap);
170     va_end(ap);
171     xkb_compose_table_unref(table);
172     return ok;
173 }
174
175 static void
176 test_seqs(struct xkb_context *ctx)
177 {
178     struct xkb_compose_table *table;
179     char *path;
180     FILE *file;
181
182     path = test_get_path("locale/en_US.UTF-8/Compose");
183     file = fopen(path, "rb");
184     assert(file);
185     free(path);
186
187     table = xkb_compose_table_new_from_file(ctx, file, "",
188                                             XKB_COMPOSE_FORMAT_TEXT_V1,
189                                             XKB_COMPOSE_COMPILE_NO_FLAGS);
190     assert(table);
191     fclose(file);
192
193     assert(test_compose_seq(table,
194         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
195         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
196         XKB_KEY_NoSymbol));
197
198     assert(test_compose_seq(table,
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_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
202         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
203         XKB_KEY_NoSymbol));
204
205     assert(test_compose_seq(table,
206         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
207         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
208         XKB_KEY_NoSymbol));
209
210     assert(test_compose_seq(table,
211         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
212         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "'",    XKB_KEY_apostrophe,
213         XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSED,   "'",    XKB_KEY_apostrophe,
214         XKB_KEY_NoSymbol));
215
216     assert(test_compose_seq(table,
217         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
218         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "´",    XKB_KEY_acute,
219         XKB_KEY_NoSymbol));
220
221     assert(test_compose_seq(table,
222         XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
223         XKB_KEY_Shift_L,        XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
224         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
225         XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
226         XKB_KEY_Control_L,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
227         XKB_KEY_T,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "@",    XKB_KEY_at,
228         XKB_KEY_NoSymbol));
229
230     assert(test_compose_seq(table,
231         XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
232         XKB_KEY_a,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
233         XKB_KEY_b,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
234         XKB_KEY_NoSymbol));
235
236     assert(test_compose_seq(table,
237         XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
238         XKB_KEY_apostrophe,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
239         XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED,  "",     XKB_KEY_NoSymbol,
240         XKB_KEY_7,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
241         XKB_KEY_Caps_Lock,      XKB_COMPOSE_FEED_IGNORED,   XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
242         XKB_KEY_NoSymbol));
243
244     xkb_compose_table_unref(table);
245
246     /* Make sure one-keysym sequences work. */
247     assert(test_compose_seq_buffer(ctx,
248         "<A>          :  \"foo\"  X \n"
249         "<B> <A>      :  \"baz\"  Y \n",
250         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "foo",   XKB_KEY_X,
251         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "foo",   XKB_KEY_X,
252         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
253         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
254         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "baz",   XKB_KEY_Y,
255         XKB_KEY_NoSymbol));
256
257     /* No sequences at all. */
258     assert(test_compose_seq_buffer(ctx,
259         "",
260         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
261         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
262         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
263         XKB_KEY_Multi_key,      XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
264         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
265         XKB_KEY_NoSymbol));
266
267     /* Only keysym - string derived from keysym. */
268     assert(test_compose_seq_buffer(ctx,
269         "<A> <B>     :  X \n"
270         "<B> <A>     :  dollar \n"
271         "<C>         :  dead_acute \n",
272         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
273         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "X",     XKB_KEY_X,
274         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
275         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "$",     XKB_KEY_dollar,
276         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "",      XKB_KEY_dead_acute,
277         XKB_KEY_NoSymbol));
278
279     /* Make sure a cancelling keysym doesn't start a new sequence. */
280     assert(test_compose_seq_buffer(ctx,
281         "<A> <B>     :  X \n"
282         "<C> <D>     :  Y \n",
283         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
284         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED, "",      XKB_KEY_NoSymbol,
285         XKB_KEY_D,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,   "",      XKB_KEY_NoSymbol,
286         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
287         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_CANCELLED, "",      XKB_KEY_NoSymbol,
288         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING, "",      XKB_KEY_NoSymbol,
289         XKB_KEY_D,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,  "Y",     XKB_KEY_Y,
290         XKB_KEY_NoSymbol));
291 }
292
293 static void
294 test_conflicting(struct xkb_context *ctx)
295 {
296     // new is prefix of old
297     assert(test_compose_seq_buffer(ctx,
298         "<A> <B> <C>  :  \"foo\"  A \n"
299         "<A> <B>      :  \"bar\"  B \n",
300         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
301         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
302         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_A,
303         XKB_KEY_NoSymbol));
304
305     // old is a prefix of new
306     assert(test_compose_seq_buffer(ctx,
307         "<A> <B>      :  \"bar\"  B \n"
308         "<A> <B> <C>  :  \"foo\"  A \n",
309         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
310         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
311         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_A,
312         XKB_KEY_NoSymbol));
313
314     // new duplicate of old
315     assert(test_compose_seq_buffer(ctx,
316         "<A> <B>      :  \"bar\"  B \n"
317         "<A> <B>      :  \"bar\"  B \n",
318         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
319         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_B,
320         XKB_KEY_C,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_NOTHING,    "",     XKB_KEY_NoSymbol,
321         XKB_KEY_NoSymbol));
322
323     // new same length as old #1
324     assert(test_compose_seq_buffer(ctx,
325         "<A> <B>      :  \"foo\"  A \n"
326         "<A> <B>      :  \"bar\"  B \n",
327         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
328         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_B,
329         XKB_KEY_NoSymbol));
330
331     // new same length as old #2
332     assert(test_compose_seq_buffer(ctx,
333         "<A> <B>      :  \"foo\"  A \n"
334         "<A> <B>      :  \"foo\"  B \n",
335         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
336         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "foo",  XKB_KEY_B,
337         XKB_KEY_NoSymbol));
338
339     // new same length as old #3
340     assert(test_compose_seq_buffer(ctx,
341         "<A> <B>      :  \"foo\"  A \n"
342         "<A> <B>      :  \"bar\"  A \n",
343         XKB_KEY_A,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
344         XKB_KEY_B,              XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_A,
345         XKB_KEY_NoSymbol));
346 }
347
348 static void
349 test_state(struct xkb_context *ctx)
350 {
351     struct xkb_compose_table *table;
352     struct xkb_compose_state *state;
353     char *path;
354     FILE *file;
355
356     path = test_get_path("locale/en_US.UTF-8/Compose");
357     file = fopen(path, "rb");
358     assert(file);
359     free(path);
360
361     table = xkb_compose_table_new_from_file(ctx, file, "",
362                                             XKB_COMPOSE_FORMAT_TEXT_V1,
363                                             XKB_COMPOSE_COMPILE_NO_FLAGS);
364     assert(table);
365     fclose(file);
366
367     state = xkb_compose_state_new(table, XKB_COMPOSE_STATE_NO_FLAGS);
368     assert(state);
369
370     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
371     xkb_compose_state_reset(state);
372     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
373     xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
374     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
375     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
376     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
377     xkb_compose_state_reset(state);
378     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
379     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
380     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
381     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
382     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
383     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
384     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
385     xkb_compose_state_feed(state, XKB_KEY_Multi_key);
386     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_CANCELLED);
387     xkb_compose_state_reset(state);
388     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
389     xkb_compose_state_feed(state, XKB_KEY_dead_acute);
390     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
391     xkb_compose_state_feed(state, XKB_KEY_A);
392     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
393     xkb_compose_state_reset(state);
394     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
395     xkb_compose_state_feed(state, XKB_KEY_dead_acute);
396     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSING);
397     xkb_compose_state_feed(state, XKB_KEY_A);
398     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_COMPOSED);
399     xkb_compose_state_reset(state);
400     xkb_compose_state_feed(state, XKB_KEY_NoSymbol);
401     assert(xkb_compose_state_get_status(state) == XKB_COMPOSE_NOTHING);
402
403     xkb_compose_state_unref(state);
404     xkb_compose_table_unref(table);
405 }
406
407 static void
408 test_XCOMPOSEFILE(struct xkb_context *ctx)
409 {
410     struct xkb_compose_table *table;
411     char *path;
412
413     path = test_get_path("locale/en_US.UTF-8/Compose");
414     setenv("XCOMPOSEFILE", path, 1);
415     free(path);
416
417     table = xkb_compose_table_new_from_locale(ctx, "blabla",
418                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
419     assert(table);
420
421     unsetenv("XCOMPOSEFILE");
422
423     assert(test_compose_seq(table,
424         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
425         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
426         XKB_KEY_NoSymbol));
427
428     xkb_compose_table_unref(table);
429 }
430
431 static void
432 test_from_locale(struct xkb_context *ctx)
433 {
434     struct xkb_compose_table *table;
435     char *path;
436
437     path = test_get_path("locale");
438     setenv("XLOCALEDIR", path, 1);
439     free(path);
440
441     /* Direct directory name match. */
442     table = xkb_compose_table_new_from_locale(ctx, "en_US.UTF-8",
443                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
444     assert(table);
445     xkb_compose_table_unref(table);
446
447     /* Direct locale name match. */
448     table = xkb_compose_table_new_from_locale(ctx, "C.UTF-8",
449                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
450     assert(table);
451     xkb_compose_table_unref(table);
452
453     /* Alias. */
454     table = xkb_compose_table_new_from_locale(ctx, "univ.utf8",
455                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
456     assert(table);
457     xkb_compose_table_unref(table);
458
459     /* Special case - C. */
460     table = xkb_compose_table_new_from_locale(ctx, "C",
461                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
462     assert(table);
463     xkb_compose_table_unref(table);
464
465     /* Bogus - not found. */
466     table = xkb_compose_table_new_from_locale(ctx, "blabla",
467                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
468     assert(!table);
469
470     unsetenv("XLOCALEDIR");
471 }
472
473
474 static void
475 test_modifier_syntax(struct xkb_context *ctx)
476 {
477     const char *table_string;
478
479     /* We don't do anything with the modifiers, but make sure we can parse
480      * them. */
481
482     assert(test_compose_seq_buffer(ctx,
483         "None <A>          : X \n"
484         "Shift <B>         : Y \n"
485         "Ctrl <C>          : Y \n"
486         "Alt <D>           : Y \n"
487         "Caps <E>          : Y \n"
488         "Lock <F>          : Y \n"
489         "Shift Ctrl <G>    : Y \n"
490         "~Shift <H>        : Y \n"
491         "~Shift Ctrl <I>   : Y \n"
492         "Shift ~Ctrl <J>   : Y \n"
493         "Shift ~Ctrl ~Alt <K> : Y \n"
494         "! Shift <B>       : Y \n"
495         "! Ctrl <C>        : Y \n"
496         "! Alt <D>         : Y \n"
497         "! Caps <E>        : Y \n"
498         "! Lock <F>        : Y \n"
499         "! Shift Ctrl <G>  : Y \n"
500         "! ~Shift <H>      : Y \n"
501         "! ~Shift Ctrl <I> : Y \n"
502         "! Shift ~Ctrl <J> : Y \n"
503         "! Shift ~Ctrl ~Alt <K> : Y \n"
504         "<L> ! Shift <M>   : Y \n"
505         "None <N> ! Shift <O> : Y \n"
506         "None <P> ! Shift <Q> : Y \n",
507         XKB_KEY_NoSymbol));
508
509     fprintf(stderr, "<START bad input string>\n");
510     table_string =
511         "! None <A>        : X \n"
512         "! Foo <B>         : X \n"
513         "None ! Shift <C>  : X \n"
514         "! ! <D>           : X \n"
515         "! ~ <E>           : X \n"
516         "! ! <F>           : X \n"
517         "! Ctrl ! Ctrl <G> : X \n"
518         "<H> !             : X \n"
519         "<I> None          : X \n"
520         "None None <J>     : X \n"
521         "<K>               : !Shift X \n";
522     assert(!xkb_compose_table_new_from_buffer(ctx, table_string,
523                                               strlen(table_string), "C",
524                                               XKB_COMPOSE_FORMAT_TEXT_V1,
525                                               XKB_COMPOSE_COMPILE_NO_FLAGS));
526     fprintf(stderr, "<END bad input string>\n");
527 }
528
529 static void
530 test_include(struct xkb_context *ctx)
531 {
532     char *path, *table_string;
533
534     path = test_get_path("locale/en_US.UTF-8/Compose");
535     assert(path);
536
537     /* We don't have a mechanism to change the include paths like we
538      * have for keymaps. So we must include the full path. */
539     table_string = asprintf_safe("<dead_tilde> <space>   : \"foo\" X\n"
540                                  "include \"%s\"\n"
541                                  "<dead_tilde> <dead_tilde> : \"bar\" Y\n", path);
542     assert(table_string);
543
544     assert(test_compose_seq_buffer(ctx, table_string,
545         /* No conflict. */
546         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
547         XKB_KEY_dead_acute,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "´",    XKB_KEY_acute,
548
549         /* Comes before - doesn't override. */
550         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
551         XKB_KEY_space,          XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "~",    XKB_KEY_asciitilde,
552
553         /* Comes after - does override. */
554         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
555         XKB_KEY_dead_tilde,     XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_Y,
556
557         XKB_KEY_NoSymbol));
558
559     free(path);
560     free(table_string);
561 }
562
563 static void
564 test_override(struct xkb_context *ctx)
565 {
566     const char *table_string = "<dead_circumflex> <dead_circumflex> : \"foo\" X\n"
567                                "<dead_circumflex> <e> : \"bar\" Y\n"
568                                "<dead_circumflex> <dead_circumflex> <e> : \"baz\" Z\n";
569
570     assert(test_compose_seq_buffer(ctx, table_string,
571         /* Comes after - does override. */
572         XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
573         XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
574         XKB_KEY_e,               XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "baz",  XKB_KEY_Z,
575
576         /* Override does not affect sibling nodes */
577         XKB_KEY_dead_circumflex, XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSING,  "",     XKB_KEY_NoSymbol,
578         XKB_KEY_e,               XKB_COMPOSE_FEED_ACCEPTED,  XKB_COMPOSE_COMPOSED,   "bar",  XKB_KEY_Y,
579
580         XKB_KEY_NoSymbol));
581 }
582
583 static bool
584 test_eq_entry_va(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym_ref, const char *utf8_ref, va_list ap)
585 {
586     assert (entry != NULL);
587
588     assert (xkb_compose_table_entry_keysym(entry) == keysym_ref);
589
590     const char *utf8 = xkb_compose_table_entry_utf8(entry);
591     assert (utf8 && utf8_ref && strcmp(utf8, utf8_ref) == 0);
592
593     size_t nsyms;
594     const xkb_keysym_t *sequence = xkb_compose_table_entry_sequence(entry, &nsyms);
595
596     xkb_keysym_t keysym;
597     for (unsigned k = 0; ; k++) {
598         keysym = va_arg(ap, xkb_keysym_t);
599         if (keysym == XKB_KEY_NoSymbol) {
600             return (k == nsyms - 1);
601         }
602         assert (k < nsyms);
603         assert (keysym == sequence[k]);
604     }
605 }
606
607 static bool
608 test_eq_entry(struct xkb_compose_table_entry *entry, xkb_keysym_t keysym, const char *utf8, ...)
609 {
610     va_list ap;
611     bool ok;
612     va_start(ap, utf8);
613     ok = test_eq_entry_va(entry, keysym, utf8, ap);
614     va_end(ap);
615     return ok;
616 }
617
618 static void
619 test_traverse(struct xkb_context *ctx)
620 {
621     struct xkb_compose_table *table;
622
623     const char *buffer = "<dead_circumflex> <dead_circumflex> : \"foo\" X\n"
624                          "<Ahook> <x> : \"foobar\"\n"
625                          "<Multi_key> <o> <e> : oe\n"
626                          "<dead_circumflex> <e> : \"bar\" Y\n"
627                          "<Multi_key> <a> <e> : \"æ\" ae\n"
628                          "<dead_circumflex> <a> : \"baz\" Z\n"
629                          "<dead_acute> <e> : \"é\" eacute\n"
630                          "<Multi_key> <a> <a> <c>: \"aac\"\n"
631                          "<Multi_key> <a> <a> <b>: \"aab\"\n"
632                          "<Multi_key> <a> <a> <a>: \"aaa\"\n";
633
634     table = xkb_compose_table_new_from_buffer(ctx, buffer, strlen(buffer), "",
635                                               XKB_COMPOSE_FORMAT_TEXT_V1,
636                                               XKB_COMPOSE_COMPILE_NO_FLAGS);
637     assert(table);
638
639     struct xkb_compose_table_iterator *iter = xkb_compose_table_iterator_new(table);
640
641     test_eq_entry(xkb_compose_table_iterator_next(iter),
642                   XKB_KEY_eacute, "é",
643                   XKB_KEY_dead_acute, XKB_KEY_e, XKB_KEY_NoSymbol);
644
645     test_eq_entry(xkb_compose_table_iterator_next(iter),
646                   XKB_KEY_Z, "baz",
647                   XKB_KEY_dead_circumflex, XKB_KEY_a, XKB_KEY_NoSymbol);
648
649     test_eq_entry(xkb_compose_table_iterator_next(iter),
650                   XKB_KEY_Y, "bar",
651                   XKB_KEY_dead_circumflex, XKB_KEY_e, XKB_KEY_NoSymbol);
652
653     test_eq_entry(xkb_compose_table_iterator_next(iter),
654                   XKB_KEY_X, "foo",
655                   XKB_KEY_dead_circumflex, XKB_KEY_dead_circumflex, XKB_KEY_NoSymbol);
656
657     test_eq_entry(xkb_compose_table_iterator_next(iter),
658                   XKB_KEY_NoSymbol, "aaa",
659                   XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_a, XKB_KEY_NoSymbol);
660
661     test_eq_entry(xkb_compose_table_iterator_next(iter),
662                   XKB_KEY_NoSymbol, "aab",
663                   XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_b, XKB_KEY_NoSymbol);
664
665     test_eq_entry(xkb_compose_table_iterator_next(iter),
666                   XKB_KEY_NoSymbol, "aac",
667                   XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_a, XKB_KEY_c, XKB_KEY_NoSymbol);
668
669     test_eq_entry(xkb_compose_table_iterator_next(iter),
670                   XKB_KEY_ae, "æ",
671                   XKB_KEY_Multi_key, XKB_KEY_a, XKB_KEY_e, XKB_KEY_NoSymbol);
672
673     test_eq_entry(xkb_compose_table_iterator_next(iter),
674                   XKB_KEY_oe, "",
675                   XKB_KEY_Multi_key, XKB_KEY_o, XKB_KEY_e, XKB_KEY_NoSymbol);
676
677     test_eq_entry(xkb_compose_table_iterator_next(iter),
678                   XKB_KEY_NoSymbol, "foobar",
679                   XKB_KEY_Ahook, XKB_KEY_x, XKB_KEY_NoSymbol);
680
681     assert (xkb_compose_table_iterator_next(iter) == NULL);
682
683     xkb_compose_table_iterator_free(iter);
684     xkb_compose_table_unref(table);
685 }
686
687 int
688 main(int argc, char *argv[])
689 {
690     struct xkb_context *ctx;
691
692     ctx = test_get_context(CONTEXT_NO_FLAG);
693     assert(ctx);
694
695     /*
696      * Ensure no environment variables but “top_srcdir” is set. This ensures
697      * that user Compose file paths are unset before the tests and set
698      * explicitely when necessary.
699      */
700 #ifdef __linux__
701     const char *srcdir = getenv("top_srcdir");
702     clearenv();
703     setenv("top_srcdir", srcdir, 1);
704 #else
705     unsetenv("XCOMPOSEFILE");
706     unsetenv("XDG_CONFIG_HOME");
707     unsetenv("HOME");
708     unsetenv("XLOCALEDIR");
709 #endif
710
711     test_seqs(ctx);
712     test_conflicting(ctx);
713     test_XCOMPOSEFILE(ctx);
714     test_from_locale(ctx);
715     test_state(ctx);
716     test_modifier_syntax(ctx);
717     test_include(ctx);
718     test_override(ctx);
719     test_traverse(ctx);
720
721     xkb_context_unref(ctx);
722     return 0;
723 }