1#include <gtk/gtk.h>
2#include <locale.h>
3
4#include <glib/gstdio.h>
5
6#include "../gtk/gtkcomposetable.h"
7#include "../gtk/gtkimcontextsimpleseqs.h"
8#include "testsuite/testutils.h"
9
10static void
11append_escaped (GString *str,
12 const char *s)
13{
14 for (const char *p = s; *p; p = g_utf8_next_char (p))
15 {
16 gunichar ch = g_utf8_get_char (p);
17 if (ch == '"')
18 g_string_append (string: str, val: "\\\"");
19 else if (ch == '\\')
20 g_string_append (string: str, val: "\\\\");
21 else if (g_unichar_isprint (c: ch))
22 g_string_append_unichar (string: str, wc: ch);
23 else
24 {
25 guint n[8] = { 0, };
26 int i = 0;
27 while (ch != 0)
28 {
29 n[i++] = ch & 7;
30 ch = ch >> 3;
31 }
32 for (; i >= 0; i--)
33 g_string_append_printf (string: str, format: "\\%o", n[i]);
34 }
35 }
36}
37
38static void
39print_sequence (gunichar *sequence,
40 int len,
41 const char *value,
42 gpointer data)
43{
44 GString *str = data;
45
46 for (int j = 0; j < len; j++)
47 g_string_append_printf (string: str, format: "<U%x> ", sequence[j]);
48
49 g_string_append (string: str, val: ": \"");
50 append_escaped (str, s: value);
51 g_string_append (string: str, val: "\"");
52
53 if (g_utf8_strlen (p: value, max: -1) == 1)
54 {
55 gunichar ch = g_utf8_get_char (p: value);
56 g_string_append_printf (string: str, format: " # U%x", ch);
57 }
58
59 g_string_append_c (str, '\n');
60}
61
62static char *
63gtk_compose_table_print (GtkComposeTable *table)
64{
65 GString *str;
66
67 str = g_string_new (init: "");
68
69 g_string_append_printf (string: str, format: "# n_sequences: %d\n# max_seq_len: %d\n# n_index_size: %d\n# data_size: %d\n# n_chars: %d\n",
70 table->n_sequences,
71 table->max_seq_len,
72 table->n_index_size,
73 table->data_size,
74 table->n_chars);
75
76 gtk_compose_table_foreach (table, callback: print_sequence, data: str);
77
78 return g_string_free (string: str, FALSE);
79}
80
81static void
82generate_output (const char *file)
83{
84 GtkComposeTable *table;
85 char *output;
86
87 table = gtk_compose_table_parse (compose_file: file, NULL);
88 output = gtk_compose_table_print (table);
89
90 g_print (format: "%s", output);
91}
92
93static void
94compose_table_compare (gconstpointer data)
95{
96 const char *basename = data;
97 GtkComposeTable *table;
98 char *file;
99 char *expected;
100 char *output;
101 char *diff;
102 GError *error = NULL;
103
104 file = g_test_build_filename (file_type: G_TEST_DIST, first_path: "compose", basename, NULL);
105 expected = g_strconcat (string1: file, ".expected", NULL);
106
107 table = gtk_compose_table_parse (compose_file: file, NULL);
108 output = gtk_compose_table_print (table);
109
110 diff = diff_with_file (file1: expected, text: output, len: -1, error: &error);
111 g_assert_no_error (error);
112
113 if (diff && diff[0])
114 {
115 g_print (format: "Resulting output doesn't match reference:\n%s", diff);
116 g_test_fail ();
117 }
118
119 g_free (mem: output);
120 g_free (mem: file);
121 g_free (mem: expected);
122}
123
124static void
125compose_table_cycle (void)
126{
127 if (g_test_subprocess ())
128 {
129 char *file;
130 GtkComposeTable *table;
131
132 file = g_test_build_filename (file_type: G_TEST_DIST, first_path: "compose", "cycle", NULL);
133
134 table = gtk_compose_table_parse (compose_file: file, NULL);
135 g_assert_nonnull (table);
136 g_free (mem: file);
137
138 return;
139 }
140
141 g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0);
142 g_test_trap_assert_stderr ("*include cycle detected*");
143 g_test_trap_assert_failed ();
144}
145
146static void
147compose_table_nofile (void)
148{
149 if (g_test_subprocess ())
150 {
151 char *file;
152 GtkComposeTable *table;
153
154 file = g_build_filename (first_element: g_test_get_dir (file_type: G_TEST_DIST), "compose", "nofile", NULL);
155
156 table = gtk_compose_table_parse (compose_file: file, NULL);
157 g_assert_nonnull (table);
158 g_free (mem: file);
159
160 return;
161 }
162
163 g_test_trap_subprocess (NULL, usec_timeout: 0, test_flags: 0);
164 g_test_trap_assert_stderr ("*No such file or directory*");
165 g_test_trap_assert_failed ();
166}
167
168/* Check matching against a small table */
169static void
170compose_table_match (void)
171{
172 GtkComposeTable *table;
173 char *file;
174 guint buffer[8] = { 0, };
175 gboolean finish, match, ret;
176 GString *output;
177
178 output = g_string_new (init: "");
179
180 file = g_build_filename (first_element: g_test_get_dir (file_type: G_TEST_DIST), "compose", "match", NULL);
181
182 table = gtk_compose_table_parse (compose_file: file, NULL);
183
184 buffer[0] = GDK_KEY_Multi_key;
185 buffer[1] = 0;
186 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 1, compose_finish: &finish, compose_match: &match, output);
187 g_assert_true (ret);
188 g_assert_false (finish);
189 g_assert_false (match);
190 g_assert_true (output->len == 0);
191
192 buffer[0] = GDK_KEY_a;
193 buffer[1] = 0;
194 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 1, compose_finish: &finish, compose_match: &match, output);
195 g_assert_false (ret);
196 g_assert_false (finish);
197 g_assert_false (match);
198 g_assert_true (output->len == 0);
199
200 buffer[0] = GDK_KEY_Multi_key;
201 buffer[1] = GDK_KEY_s;
202 buffer[2] = GDK_KEY_e;
203 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 3, compose_finish: &finish, compose_match: &match, output);
204 g_assert_true (ret);
205 g_assert_false (finish);
206 g_assert_false (match);
207 g_assert_true (output->len == 0);
208
209 buffer[0] = GDK_KEY_Multi_key;
210 buffer[1] = GDK_KEY_s;
211 buffer[2] = GDK_KEY_e;
212 buffer[3] = GDK_KEY_q;
213 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 4, compose_finish: &finish, compose_match: &match, output);
214 g_assert_true (ret);
215 g_assert_false (finish);
216 g_assert_true (match);
217 g_assert_cmpstr (output->str, ==, "!");
218
219 g_string_set_size (string: output, len: 0);
220
221 buffer[0] = GDK_KEY_Multi_key;
222 buffer[1] = GDK_KEY_s;
223 buffer[2] = GDK_KEY_e;
224 buffer[3] = GDK_KEY_q;
225 buffer[4] = GDK_KEY_u;
226 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 5, compose_finish: &finish, compose_match: &match, output);
227 g_assert_true (ret);
228 g_assert_true (finish);
229 g_assert_true (match);
230 g_assert_cmpstr (output->str, ==, "?");
231
232 g_string_set_size (string: output, len: 0);
233
234 buffer[0] = GDK_KEY_Multi_key;
235 buffer[1] = GDK_KEY_l;
236 buffer[2] = GDK_KEY_o;
237 buffer[3] = GDK_KEY_n;
238 buffer[4] = GDK_KEY_g;
239 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 5, compose_finish: &finish, compose_match: &match, output);
240 g_assert_true (ret);
241 g_assert_true (finish);
242 g_assert_true (match);
243 g_assert_cmpstr (output->str, ==, "this is a long replacement string");
244
245 g_string_free (string: output, TRUE);
246 g_free (mem: file);
247}
248
249extern const GtkComposeTable builtin_compose_table;
250
251/* just check some random sequences */
252static void
253compose_table_match_builtin (void)
254{
255 const GtkComposeTable *table = &builtin_compose_table;
256 guint buffer[8] = { 0, };
257 gboolean finish, match, ret;
258 GString *s;
259
260 buffer[0] = GDK_KEY_Multi_key;
261 buffer[1] = 0;
262
263 s = g_string_new (init: "");
264
265 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 1, compose_finish: &finish, compose_match: &match, output: s);
266 g_assert_true (ret);
267 g_assert_false (finish);
268 g_assert_false (match);
269 g_assert_true (s->len == 0);
270
271 buffer[0] = GDK_KEY_a;
272 buffer[1] = GDK_KEY_b;
273 buffer[2] = GDK_KEY_c;
274 buffer[3] = 0;
275
276 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 3, compose_finish: &finish, compose_match: &match, output: s);
277 g_assert_false (ret);
278 g_assert_false (finish);
279 g_assert_false (match);
280 g_assert_true (s->len == 0);
281
282 buffer[0] = GDK_KEY_Multi_key;
283 buffer[1] = GDK_KEY_parenleft;
284 buffer[2] = GDK_KEY_j;
285 buffer[3] = GDK_KEY_parenright;
286 buffer[4] = 0;
287
288 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 4, compose_finish: &finish, compose_match: &match, output: s);
289 g_assert_true (ret);
290 g_assert_true (finish);
291 g_assert_true (match);
292 g_assert_cmpstr (s->str, ==, "ⓙ"); /* CIRCLED LATIN SMALL LETTER J */
293
294 buffer[0] = GDK_KEY_dead_acute;
295 buffer[1] = GDK_KEY_space;
296 buffer[2] = 0;
297
298 g_string_set_size (string: s, len: 0);
299
300 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 2, compose_finish: &finish, compose_match: &match, output: s);
301 g_assert_true (ret);
302 g_assert_true (finish);
303 g_assert_true (match);
304 g_assert_cmpstr (s->str, ==, "'");
305
306 buffer[0] = GDK_KEY_dead_acute;
307 buffer[1] = GDK_KEY_dead_acute;
308 buffer[2] = 0;
309
310 g_string_set_size (string: s, len: 0);
311
312 ret = gtk_compose_table_check (table, compose_buffer: buffer, n_compose: 2, compose_finish: &finish, compose_match: &match, output: s);
313 g_assert_true (ret);
314 g_assert_true (finish);
315 g_assert_true (match);
316 g_assert_cmpstr (s->str, ==, "´");
317
318 g_string_free (string: s, TRUE);
319}
320
321static void
322match_algorithmic (void)
323{
324 guint buffer[8] = { 0, };
325 gboolean ret;
326 GString *output;
327
328 output = g_string_new (init: "");
329
330 buffer[0] = GDK_KEY_a;
331 buffer[1] = GDK_KEY_b;
332
333 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 2, output);
334 g_assert_false (ret);
335 g_assert_cmpstr (output->str, ==, "");
336
337 buffer[0] = GDK_KEY_dead_abovering;
338 buffer[1] = GDK_KEY_A;
339
340 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 2, output);
341 g_assert_true (ret);
342 g_assert_cmpstr (output->str, ==, "Å");
343
344 buffer[0] = GDK_KEY_A;
345 buffer[1] = GDK_KEY_dead_abovering;
346
347 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 2, output);
348 g_assert_false (ret);
349 g_assert_cmpstr (output->str, ==, "");
350
351 buffer[0] = GDK_KEY_dead_dasia;
352 buffer[1] = GDK_KEY_dead_perispomeni;
353 buffer[2] = GDK_KEY_Greek_alpha;
354
355 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 3, output);
356 g_assert_true (ret);
357 g_assert_cmpstr (output->str, ==, "ᾶ\xcc\x94");
358
359 buffer[0] = GDK_KEY_dead_perispomeni;
360 buffer[1] = GDK_KEY_dead_dasia;
361 buffer[2] = GDK_KEY_Greek_alpha;
362
363 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 3, output);
364 g_assert_true (ret);
365 g_assert_cmpstr (output->str, ==, "ἇ");
366
367 buffer[0] = GDK_KEY_dead_acute;
368 buffer[1] = GDK_KEY_dead_cedilla;
369 buffer[2] = GDK_KEY_c;
370
371 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 2, output);
372 g_assert_true (ret);
373 g_assert_cmpstr (output->str, ==, "");
374
375 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 3, output);
376 g_assert_true (ret);
377 g_assert_cmpstr (output->str, ==, "ḉ");
378
379 buffer[0] = GDK_KEY_dead_cedilla;
380 buffer[1] = GDK_KEY_dead_acute;
381 buffer[2] = GDK_KEY_c;
382
383 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 3, output);
384 g_assert_true (ret);
385 g_assert_cmpstr (output->str, ==, "ḉ");
386
387 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 2, output);
388 g_assert_true (ret);
389
390 buffer[0] = GDK_KEY_dead_acute;
391 buffer[1] = GDK_KEY_dead_cedilla;
392 buffer[2] = GDK_KEY_dead_grave;
393
394 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 3, output);
395 g_assert_true (ret);
396 g_assert_cmpstr (output->str, ==, "");
397
398 buffer[0] = GDK_KEY_dead_diaeresis;
399 buffer[1] = GDK_KEY_a;
400
401 ret = gtk_check_algorithmically (compose_buffer: buffer, n_compose: 2, output);
402 g_assert_true (ret);
403 g_assert_cmpstr (output->str, ==, "ä");
404
405 g_string_free (string: output, TRUE);
406}
407
408int
409main (int argc, char *argv[])
410{
411 if (argc == 3 && strcmp (s1: argv[1], s2: "--generate") == 0)
412 {
413 gtk_disable_setlocale();
414 setlocale (LC_ALL, locale: "en_US.UTF-8");
415
416 gtk_init ();
417
418 /* Ensure that the builtin table is initialized */
419 GtkIMContext *ctx = gtk_im_context_simple_new ();
420 g_object_unref (object: ctx);
421
422 generate_output (file: argv[2]);
423 return 0;
424 }
425
426 gtk_test_init (argcp: &argc, argvp: &argv, NULL);
427
428 /* Ensure that the builtin table is initialized */
429 GtkIMContext *ctx = gtk_im_context_simple_new ();
430 g_object_unref (object: ctx);
431
432 g_test_add_data_func (testpath: "/compose-table/basic", test_data: "basic", test_func: compose_table_compare);
433 g_test_add_data_func (testpath: "/compose-table/long", test_data: "long", test_func: compose_table_compare);
434 g_test_add_data_func (testpath: "/compose-table/octal", test_data: "octal", test_func: compose_table_compare);
435 g_test_add_data_func (testpath: "/compose-table/hex", test_data: "hex", test_func: compose_table_compare);
436 g_test_add_data_func (testpath: "/compose-table/codepoint", test_data: "codepoint", test_func: compose_table_compare);
437 g_test_add_data_func (testpath: "/compose-table/multi", test_data: "multi", test_func: compose_table_compare);
438 g_test_add_data_func (testpath: "/compose-table/strings", test_data: "strings", test_func: compose_table_compare);
439 g_test_add_data_func (testpath: "/compose-table/include", test_data: "include", test_func: compose_table_compare);
440 g_test_add_data_func (testpath: "/compose-table/system", test_data: "system", test_func: compose_table_compare);
441 g_test_add_func (testpath: "/compose-table/include-cycle", test_func: compose_table_cycle);
442 g_test_add_func (testpath: "/compose-table/include-nofile", test_func: compose_table_nofile);
443 g_test_add_func (testpath: "/compose-table/match", test_func: compose_table_match);
444 g_test_add_func (testpath: "/compose-table/match-builtin", test_func: compose_table_match_builtin);
445 g_test_add_func (testpath: "/compose-table/match-algorithmic", test_func: match_algorithmic);
446
447 return g_test_run ();
448}
449

source code of gtk/testsuite/gtk/composetable.c