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
10 static void
append_escaped(GString * str,const char * s)11 append_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 (str, "\\\"");
19 else if (ch == '\\')
20 g_string_append (str, "\\\\");
21 else if (g_unichar_isprint (ch))
22 g_string_append_unichar (str, 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 (str, "\\%o", n[i]);
34 }
35 }
36 }
37
38 static void
print_sequence(gunichar * sequence,int len,const char * value,gpointer data)39 print_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 (str, "<U%x> ", sequence[j]);
48
49 g_string_append (str, ": \"");
50 append_escaped (str, value);
51 g_string_append (str, "\"");
52
53 if (g_utf8_strlen (value, -1) == 1)
54 {
55 gunichar ch = g_utf8_get_char (value);
56 g_string_append_printf (str, " # U%x", ch);
57 }
58
59 g_string_append_c (str, '\n');
60 }
61
62 static char *
gtk_compose_table_print(GtkComposeTable * table)63 gtk_compose_table_print (GtkComposeTable *table)
64 {
65 GString *str;
66
67 str = g_string_new ("");
68
69 g_string_append_printf (str, "# 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, print_sequence, str);
77
78 return g_string_free (str, FALSE);
79 }
80
81 static void
generate_output(const char * file)82 generate_output (const char *file)
83 {
84 GtkComposeTable *table;
85 char *output;
86
87 table = gtk_compose_table_parse (file, NULL);
88 output = gtk_compose_table_print (table);
89
90 g_print ("%s", output);
91 }
92
93 static void
compose_table_compare(gconstpointer data)94 compose_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 (G_TEST_DIST, "compose", basename, NULL);
105 expected = g_strconcat (file, ".expected", NULL);
106
107 table = gtk_compose_table_parse (file, NULL);
108 output = gtk_compose_table_print (table);
109
110 diff = diff_with_file (expected, output, -1, &error);
111 g_assert_no_error (error);
112
113 if (diff && diff[0])
114 {
115 g_print ("Resulting output doesn't match reference:\n%s", diff);
116 g_test_fail ();
117 }
118
119 g_free (output);
120 g_free (file);
121 g_free (expected);
122 }
123
124 static void
compose_table_cycle(void)125 compose_table_cycle (void)
126 {
127 if (g_test_subprocess ())
128 {
129 char *file;
130 GtkComposeTable *table;
131
132 file = g_test_build_filename (G_TEST_DIST, "compose", "cycle", NULL);
133
134 table = gtk_compose_table_parse (file, NULL);
135 g_assert_nonnull (table);
136 g_free (file);
137
138 return;
139 }
140
141 g_test_trap_subprocess (NULL, 0, 0);
142 g_test_trap_assert_stderr ("*include cycle detected*");
143 g_test_trap_assert_failed ();
144 }
145
146 static void
compose_table_nofile(void)147 compose_table_nofile (void)
148 {
149 if (g_test_subprocess ())
150 {
151 char *file;
152 GtkComposeTable *table;
153
154 file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "nofile", NULL);
155
156 table = gtk_compose_table_parse (file, NULL);
157 g_assert_nonnull (table);
158 g_free (file);
159
160 return;
161 }
162
163 g_test_trap_subprocess (NULL, 0, 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 */
169 static void
compose_table_match(void)170 compose_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 ("");
179
180 file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "match", NULL);
181
182 table = gtk_compose_table_parse (file, NULL);
183
184 buffer[0] = GDK_KEY_Multi_key;
185 buffer[1] = 0;
186 ret = gtk_compose_table_check (table, buffer, 1, &finish, &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, buffer, 1, &finish, &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, buffer, 3, &finish, &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, buffer, 4, &finish, &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 (output, 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, buffer, 5, &finish, &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 (output, 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, buffer, 5, &finish, &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 (output, TRUE);
246 g_free (file);
247 }
248
249 extern const GtkComposeTable builtin_compose_table;
250
251 /* just check some random sequences */
252 static void
compose_table_match_builtin(void)253 compose_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 ("");
264
265 ret = gtk_compose_table_check (table, buffer, 1, &finish, &match, 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, buffer, 3, &finish, &match, 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, buffer, 4, &finish, &match, 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 (s, 0);
299
300 ret = gtk_compose_table_check (table, buffer, 2, &finish, &match, 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 (s, 0);
311
312 ret = gtk_compose_table_check (table, buffer, 2, &finish, &match, 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 (s, TRUE);
319 }
320
321 static void
match_algorithmic(void)322 match_algorithmic (void)
323 {
324 guint buffer[8] = { 0, };
325 gboolean ret;
326 GString *output;
327
328 output = g_string_new ("");
329
330 buffer[0] = GDK_KEY_a;
331 buffer[1] = GDK_KEY_b;
332
333 ret = gtk_check_algorithmically (buffer, 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 (buffer, 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 (buffer, 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 (buffer, 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 (buffer, 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 (buffer, 2, output);
372 g_assert_true (ret);
373 g_assert_cmpstr (output->str, ==, "");
374
375 ret = gtk_check_algorithmically (buffer, 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 (buffer, 3, output);
384 g_assert_true (ret);
385 g_assert_cmpstr (output->str, ==, "ḉ");
386
387 ret = gtk_check_algorithmically (buffer, 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 (buffer, 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 (buffer, 2, output);
402 g_assert_true (ret);
403 g_assert_cmpstr (output->str, ==, "ä");
404
405 g_string_free (output, TRUE);
406 }
407
408 int
main(int argc,char * argv[])409 main (int argc, char *argv[])
410 {
411 if (argc == 3 && strcmp (argv[1], "--generate") == 0)
412 {
413 gtk_disable_setlocale();
414 setlocale (LC_ALL, "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 (ctx);
421
422 generate_output (argv[2]);
423 return 0;
424 }
425
426 gtk_test_init (&argc, &argv, NULL);
427
428 /* Ensure that the builtin table is initialized */
429 GtkIMContext *ctx = gtk_im_context_simple_new ();
430 g_object_unref (ctx);
431
432 g_test_add_data_func ("/compose-table/basic", "basic", compose_table_compare);
433 g_test_add_data_func ("/compose-table/long", "long", compose_table_compare);
434 g_test_add_data_func ("/compose-table/octal", "octal", compose_table_compare);
435 g_test_add_data_func ("/compose-table/hex", "hex", compose_table_compare);
436 g_test_add_data_func ("/compose-table/codepoint", "codepoint", compose_table_compare);
437 g_test_add_data_func ("/compose-table/multi", "multi", compose_table_compare);
438 g_test_add_data_func ("/compose-table/strings", "strings", compose_table_compare);
439 g_test_add_data_func ("/compose-table/include", "include", compose_table_compare);
440 g_test_add_data_func ("/compose-table/system", "system", compose_table_compare);
441 g_test_add_func ("/compose-table/include-cycle", compose_table_cycle);
442 g_test_add_func ("/compose-table/include-nofile", compose_table_nofile);
443 g_test_add_func ("/compose-table/match", compose_table_match);
444 g_test_add_func ("/compose-table/match-builtin", compose_table_match_builtin);
445 g_test_add_func ("/compose-table/match-algorithmic", match_algorithmic);
446
447 return g_test_run ();
448 }
449