test: use termios instead of system() for disabling terminal echo
[platform/upstream/libxkbcommon.git] / test / common.c
1 /*
2  * Copyright © 2009 Dan Nicholson <dbn.lists@gmail.com>
3  * Copyright © 2012 Intel Corporation
4  * Copyright © 2012 Ran Benita <ran234@gmail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * Except as contained in this notice, the names of the authors or their
24  * institutions shall not be used in advertising or otherwise to promote the
25  * sale, use or other dealings in this Software without prior written
26  * authorization from the authors.
27  *
28  * Author: Dan Nicholson <dbn.lists@gmail.com>
29  *         Daniel Stone <daniel@fooishbar.org>
30  *         Ran Benita <ran234@gmail.com>
31  */
32
33 #include <limits.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <termios.h>
39
40 #include "test.h"
41
42 /*
43  * Test a sequence of keysyms, resulting from a sequence of key presses,
44  * against the keysyms they're supposed to generate.
45  *
46  * - Each test runs with a clean state.
47  * - Each line in the test is made up of:
48  *   + A keycode, given as a KEY_* from linux/input.h.
49  *   + A direction - DOWN for press, UP for release, BOTH for
50  *     immediate press + release, REPEAT to just get the syms.
51  *   + A sequence of keysyms that should result from this keypress.
52  *
53  * The vararg format is:
54  * <KEY_*>  <DOWN | UP | BOTH>  <XKB_KEY_* (zero or more)>  <NEXT | FINISH>
55  *
56  * See below for examples.
57  */
58 int
59 test_key_seq_va(struct xkb_keymap *keymap, va_list ap)
60 {
61     struct xkb_state *state;
62
63     xkb_keycode_t kc;
64     int op;
65     xkb_keysym_t keysym;
66
67     const xkb_keysym_t *syms;
68     xkb_keysym_t sym;
69     unsigned int nsyms, i;
70     char ksbuf[64];
71
72     fprintf(stderr, "----\n");
73
74     state = xkb_state_new(keymap);
75     assert(state);
76
77     for (;;) {
78         kc = va_arg(ap, int) + EVDEV_OFFSET;
79         op = va_arg(ap, int);
80
81         nsyms = xkb_state_key_get_syms(state, kc, &syms);
82         if (nsyms == 1) {
83             sym = xkb_state_key_get_one_sym(state, kc);
84             syms = &sym;
85         }
86
87         fprintf(stderr, "got %u syms for keycode %u: [", nsyms, kc);
88
89         if (op == DOWN || op == BOTH)
90             xkb_state_update_key(state, kc, XKB_KEY_DOWN);
91         if (op == UP || op == BOTH)
92             xkb_state_update_key(state, kc, XKB_KEY_UP);
93
94         for (i = 0; i < nsyms; i++) {
95             keysym = va_arg(ap, int);
96             xkb_keysym_get_name(syms[i], ksbuf, sizeof(ksbuf));
97             fprintf(stderr, "%s%s", (i != 0) ? ", " : "", ksbuf);
98
99             if (keysym == FINISH || keysym == NEXT) {
100                 xkb_keysym_get_name(syms[i], ksbuf, sizeof(ksbuf));
101                 fprintf(stderr, "Did not expect keysym: %s.\n", ksbuf);
102                 goto fail;
103             }
104
105             if (keysym != syms[i]) {
106                 xkb_keysym_get_name(keysym, ksbuf, sizeof(ksbuf));
107                 fprintf(stderr, "Expected keysym: %s. ", ksbuf);;
108                 xkb_keysym_get_name(syms[i], ksbuf, sizeof(ksbuf));
109                 fprintf(stderr, "Got keysym: %s.\n", ksbuf);;
110                 goto fail;
111             }
112         }
113
114         if (nsyms == 0) {
115             keysym = va_arg(ap, int);
116             if (keysym != XKB_KEY_NoSymbol) {
117                 xkb_keysym_get_name(keysym, ksbuf, sizeof(ksbuf));
118                 fprintf(stderr, "Expected %s, but got no keysyms.\n", ksbuf);
119                 goto fail;
120             }
121         }
122
123         fprintf(stderr, "]\n");
124
125         keysym = va_arg(ap, int);
126         if (keysym == NEXT)
127             continue;
128         if (keysym == FINISH)
129             break;
130
131         xkb_keysym_get_name(keysym, ksbuf, sizeof(ksbuf));
132         fprintf(stderr, "Expected keysym: %s. Didn't get it.\n", ksbuf);
133         goto fail;
134     }
135
136     xkb_state_unref(state);
137     return 1;
138
139 fail:
140     xkb_state_unref(state);
141     return 0;
142 }
143
144 int
145 test_key_seq(struct xkb_keymap *keymap, ...)
146 {
147     va_list ap;
148     int ret;
149
150     va_start(ap, keymap);
151     ret = test_key_seq_va(keymap, ap);
152     va_end(ap);
153
154     return ret;
155 }
156
157 char *
158 test_get_path(const char *path_rel)
159 {
160     char *path;
161     size_t path_len;
162     const char *srcdir = getenv("srcdir");
163
164     path_len = strlen(srcdir ? srcdir : ".") +
165                strlen(path_rel ? path_rel : "") + 12;
166     path = malloc(path_len);
167     if (!path) {
168         fprintf(stderr, "Failed to allocate path (%d chars) for %s\n",
169                 (int) path_len, path);
170         return NULL;
171     }
172     snprintf(path, path_len,
173              "%s/test/data/%s", srcdir ? srcdir : ".",
174              path_rel ? path_rel : "");
175
176     return path;
177 }
178
179 char *
180 test_read_file(const char *path_rel)
181 {
182     struct stat info;
183     char *ret, *tmp, *path;
184     int fd, count, remaining;
185
186     path = test_get_path(path_rel);
187     if (!path)
188         return NULL;
189
190     fd = open(path, O_RDONLY);
191     free(path);
192     if (fd < 0)
193         return NULL;
194
195     if (fstat(fd, &info) != 0) {
196         close(fd);
197         return NULL;
198     }
199
200     ret = malloc(info.st_size + 1);
201     if (!ret) {
202         close(fd);
203         return NULL;
204     }
205
206     remaining = info.st_size;
207     tmp = ret;
208     while ((count = read(fd, tmp, remaining))) {
209         remaining -= count;
210         tmp += count;
211     }
212     ret[info.st_size] = '\0';
213     close(fd);
214
215     if (remaining != 0) {
216         free(ret);
217         return NULL;
218     }
219
220     return ret;
221 }
222
223 struct xkb_context *
224 test_get_context(enum test_context_flags test_flags)
225 {
226     enum xkb_context_flags ctx_flags;
227     struct xkb_context *ctx;
228     char *path;
229
230     ctx_flags = XKB_CONTEXT_NO_DEFAULT_INCLUDES;
231     if (test_flags & CONTEXT_ALLOW_ENVIRONMENT_NAMES) {
232         unsetenv("XKB_DEFAULT_RULES");
233         unsetenv("XKB_DEFAULT_MODEL");
234         unsetenv("XKB_DEFAULT_LAYOUT");
235         unsetenv("XKB_DEFAULT_VARIANT");
236         unsetenv("XKB_DEFAULT_OPTIONS");
237     }
238     else {
239         ctx_flags |= XKB_CONTEXT_NO_ENVIRONMENT_NAMES;
240     }
241
242     ctx = xkb_context_new(ctx_flags);
243     if (!ctx)
244         return NULL;
245
246     path = test_get_path("");
247     if (!path)
248         return NULL;
249
250     xkb_context_include_path_append(ctx, path);
251     free(path);
252
253     return ctx;
254 }
255
256 struct xkb_keymap *
257 test_compile_file(struct xkb_context *context, const char *path_rel)
258 {
259     struct xkb_keymap *keymap;
260     FILE *file;
261     char *path;
262
263     path = test_get_path(path_rel);
264     if (!path)
265         return NULL;
266
267     file = fopen(path, "r");
268     if (!file) {
269         fprintf(stderr, "Failed to open path: %s\n", path);
270         free(path);
271         return NULL;
272     }
273     assert(file != NULL);
274
275     keymap = xkb_keymap_new_from_file(context, file,
276                                       XKB_KEYMAP_FORMAT_TEXT_V1, 0);
277     fclose(file);
278
279     if (!keymap) {
280         fprintf(stderr, "Failed to compile path: %s\n", path);
281         free(path);
282         return NULL;
283     }
284
285     fprintf(stderr, "Successfully compiled path: %s\n", path);
286     free(path);
287
288     return keymap;
289 }
290
291 struct xkb_keymap *
292 test_compile_string(struct xkb_context *context, const char *string)
293 {
294     struct xkb_keymap *keymap;
295
296     keymap = xkb_keymap_new_from_string(context, string,
297                                         XKB_KEYMAP_FORMAT_TEXT_V1, 0);
298     if (!keymap) {
299         fprintf(stderr, "Failed to compile string\n");
300         return NULL;
301     }
302
303     return keymap;
304 }
305
306 struct xkb_keymap *
307 test_compile_buffer(struct xkb_context *context, const char *buf, size_t len)
308 {
309     struct xkb_keymap *keymap;
310
311     keymap = xkb_keymap_new_from_buffer(context, buf, len,
312                                         XKB_KEYMAP_FORMAT_TEXT_V1, 0);
313     if (!keymap) {
314         fprintf(stderr, "Failed to compile keymap from memory buffer\n");
315         return NULL;
316     }
317
318     return keymap;
319 }
320
321 struct xkb_keymap *
322 test_compile_rules(struct xkb_context *context, const char *rules,
323                    const char *model, const char *layout,
324                    const char *variant, const char *options)
325 {
326     struct xkb_keymap *keymap;
327     struct xkb_rule_names rmlvo = {
328         .rules = isempty(rules) ? NULL : rules,
329         .model = isempty(model) ? NULL : model,
330         .layout = isempty(layout) ? NULL : layout,
331         .variant = isempty(variant) ? NULL : variant,
332         .options = isempty(options) ? NULL : options
333     };
334
335     if (!rules && !model && !layout && !variant && !options)
336         keymap = xkb_keymap_new_from_names(context, NULL, 0);
337     else
338         keymap = xkb_keymap_new_from_names(context, &rmlvo, 0);
339
340     if (!keymap) {
341         fprintf(stderr,
342                 "Failed to compile RMLVO: '%s', '%s', '%s', '%s', '%s'\n",
343                 rules, model, layout, variant, options);
344         return NULL;
345     }
346
347     return keymap;
348 }
349
350 void
351 test_print_keycode_state(struct xkb_state *state,
352                          struct xkb_compose_state *compose_state,
353                          xkb_keycode_t keycode)
354 {
355     struct xkb_keymap *keymap;
356
357     xkb_keysym_t sym;
358     const xkb_keysym_t *syms;
359     int nsyms;
360     char s[16];
361     xkb_layout_index_t layout;
362     enum xkb_compose_status status;
363
364     keymap = xkb_state_get_keymap(state);
365
366     nsyms = xkb_state_key_get_syms(state, keycode, &syms);
367
368     if (nsyms <= 0)
369         return;
370
371     status = XKB_COMPOSE_NOTHING;
372     if (compose_state)
373         status = xkb_compose_state_get_status(compose_state);
374
375     if (status == XKB_COMPOSE_COMPOSING || status == XKB_COMPOSE_CANCELLED)
376         return;
377
378     if (status == XKB_COMPOSE_COMPOSED) {
379         sym = xkb_compose_state_get_one_sym(compose_state);
380         syms = &sym;
381         nsyms = 1;
382     }
383     else if (nsyms == 1) {
384         sym = xkb_state_key_get_one_sym(state, keycode);
385         syms = &sym;
386     }
387
388     printf("keysyms [ ");
389     for (int i = 0; i < nsyms; i++) {
390         xkb_keysym_get_name(syms[i], s, sizeof(s));
391         printf("%-*s ", (int) sizeof(s), s);
392     }
393     printf("] ");
394
395     if (status == XKB_COMPOSE_COMPOSED)
396         xkb_compose_state_get_utf8(compose_state, s, sizeof(s));
397     else
398         xkb_state_key_get_utf8(state, keycode, s, sizeof(s));
399     printf("unicode [ %s ] ", s);
400
401     layout = xkb_state_key_get_layout(state, keycode);
402     printf("layout [ %s (%d) ] ",
403            xkb_keymap_layout_get_name(keymap, layout), layout);
404
405     printf("level [ %d ] ",
406            xkb_state_key_get_level(state, keycode, layout));
407
408     printf("mods [ ");
409     for (xkb_mod_index_t mod = 0; mod < xkb_keymap_num_mods(keymap); mod++) {
410         if (xkb_state_mod_index_is_active(state, mod,
411                                           XKB_STATE_MODS_EFFECTIVE) <= 0)
412             continue;
413         if (xkb_state_mod_index_is_consumed(state, keycode, mod))
414             printf("-%s ", xkb_keymap_mod_get_name(keymap, mod));
415         else
416             printf("%s ", xkb_keymap_mod_get_name(keymap, mod));
417     }
418     printf("] ");
419
420     printf("leds [ ");
421     for (xkb_led_index_t led = 0; led < xkb_keymap_num_leds(keymap); led++) {
422         if (xkb_state_led_index_is_active(state, led) <= 0)
423             continue;
424         printf("%s ", xkb_keymap_led_get_name(keymap, led));
425     }
426     printf("] ");
427
428     printf("\n");
429 }
430
431 void
432 test_print_state_changes(enum xkb_state_component changed)
433 {
434     if (changed == 0)
435         return;
436
437     printf("changed [ ");
438     if (changed & XKB_STATE_LAYOUT_EFFECTIVE)
439         printf("effective-layout ");
440     if (changed & XKB_STATE_LAYOUT_DEPRESSED)
441         printf("depressed-layout ");
442     if (changed & XKB_STATE_LAYOUT_LATCHED)
443         printf("latched-layout ");
444     if (changed & XKB_STATE_LAYOUT_LOCKED)
445         printf("locked-layout ");
446     if (changed & XKB_STATE_MODS_EFFECTIVE)
447         printf("effective-mods ");
448     if (changed & XKB_STATE_MODS_DEPRESSED)
449         printf("depressed-mods ");
450     if (changed & XKB_STATE_MODS_LATCHED)
451         printf("latched-mods ");
452     if (changed & XKB_STATE_MODS_LOCKED)
453         printf("locked-mods ");
454     if (changed & XKB_STATE_LEDS)
455         printf("leds ");
456     printf("]\n");
457 }
458
459 void
460 test_disable_stdin_echo(void)
461 {
462     /* Same as `stty -echo`. */
463     struct termios termios;
464     if (tcgetattr(STDIN_FILENO, &termios) == 0) {
465         termios.c_lflag &= ~ECHO;
466         (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &termios);
467     }
468 }
469
470 void
471 test_enable_stdin_echo(void)
472 {
473     /* Same as `stty echo`. */
474     struct termios termios;
475     if (tcgetattr(STDIN_FILENO, &termios) == 0) {
476         termios.c_lflag |= ECHO;
477         (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &termios);
478     }
479 }