1 #include <stdarg.h>
2 #include <stdlib.h>
3 #include <wchar.h>
4 #include <check.h>
5 
6 #include "../hangul/hangul.h"
7 
8 #define countof(x)  ((sizeof(x)) / (sizeof(x[0])))
9 
10 static bool
check_preedit(const char * keyboard,const char * input,const wchar_t * output)11 check_preedit(const char* keyboard, const char* input, const wchar_t* output)
12 {
13     HangulInputContext* ic;
14     const char* p;
15     const ucschar* preedit;
16     int res;
17 
18     ic = hangul_ic_new(keyboard);
19 
20     p = input;
21     while (*p != '\0') {
22 	hangul_ic_process(ic, *p);
23 	p++;
24     }
25 
26     preedit = hangul_ic_get_preedit_string(ic);
27 
28     res = wcscmp((const wchar_t*)preedit, output);
29 
30     hangul_ic_delete(ic);
31 
32     return res == 0;
33 }
34 
35 static bool
check_commit(const char * keyboard,const char * input,const wchar_t * output)36 check_commit(const char* keyboard, const char* input, const wchar_t* output)
37 {
38     HangulInputContext* ic;
39     const char* p;
40     const ucschar* commit;
41     int res;
42 
43     ic = hangul_ic_new(keyboard);
44 
45     p = input;
46     while (*p != '\0') {
47 	hangul_ic_process(ic, *p);
48 	p++;
49     }
50 
51     commit = hangul_ic_get_commit_string(ic);
52 
53     res = wcscmp((const wchar_t*)commit, output);
54 
55     hangul_ic_delete(ic);
56 
57     return res == 0;
58 }
59 
START_TEST(test_hangul_ic_process_2)60 START_TEST(test_hangul_ic_process_2)
61 {
62     /* ㄱㅏㅉ */
63     fail_unless(check_commit("2", "rkW", L"가"));
64     fail_unless(check_preedit("2", "rkW", L"ㅉ"));
65     /* ㅂㅓㅅㅅㅡ */
66     fail_unless(check_commit("2", "qjttm", L"벗"));
67     fail_unless(check_preedit("2", "qjttm", L"스"));
68     /* ㅂㅓㅆㅡ */
69     fail_unless(check_commit("2", "qjTm", L"버"));
70     fail_unless(check_preedit("2", "qjTm", L"쓰"));
71     /* ㅁㅏㄹㄱㅗ */
72     fail_unless(check_preedit("2", "akfr", L"맑"));
73     fail_unless(check_commit("2", "akfrh", L"말"));
74     fail_unless(check_preedit("2", "akfrh", L"고"));
75 }
76 END_TEST
77 
START_TEST(test_hangul_ic_process_2y)78 START_TEST(test_hangul_ic_process_2y)
79 {
80     /* ㅎ     */
81     fail_unless(check_preedit("2y", "g", L"ㅎ"));
82     /*   ㅗ   */
83     fail_unless(check_preedit("2y", "h", L"ㅗ"));
84     /*     ㅌ */
85     fail_unless(check_preedit("2y", "x", L"ㅌ"));
86     /* ㅂㅇ   */
87     fail_unless(check_preedit("2y", "qd", L"\x3178"));
88     /* ᄼ     */
89     fail_unless(check_preedit("2y", "Z", L"\x113c\x1160"));
90     /* ᅐ     */
91     fail_unless(check_preedit("2y", "V", L"\x1150\x1160"));
92     /* ᅝ     */
93     fail_unless(check_preedit("2y", "sg", L"\x115d\x1160"));
94 
95     /* ㄱㅏㅇ */
96     fail_unless(check_preedit("2y", "rkd", L"강"));
97     /* ㄹㅐ   */
98     fail_unless(check_preedit("2y", "fo", L"래"));
99     /* ㅎ. ㄴ */
100     fail_unless(check_preedit("2y", "gKs", L"\x1112\x119e\x11ab"));
101     /* ㅂㅂㅇㅏㅁㅅㅅ */
102     fail_unless(check_preedit("2y", "qqdhatt", L"\x112c\x1169\x11de"));
103     /* ㅂㅂㅇㅏㅁㅅㅅㅛ */
104     fail_unless(check_commit("2y", "qqdhatty", L"\x112c\x1169\x11dd"));
105     fail_unless(check_preedit("2y", "qqdhatty", L"쇼"));
106     /* ㅂㅂㅇㅏㅁㅆㅛ */
107     fail_unless(check_commit("2y", "qqdhaTy", L"\x112c\x1169\x11b7"));
108     fail_unless(check_preedit("2y", "qqdhaTy", L"쑈"));
109     /* 옛이응 처리 */
110     /* ㅇㅇㅏㅇㅇㅏ */
111     fail_unless(check_commit("2y", "ddkdd", L"\x1147\x1161\x11bc"));
112     fail_unless(check_preedit("2y", "ddkdd", L"ㅇ"));
113     /* ㄱㅏㆁㆁ */
114     fail_unless(check_preedit("2y", "rkDD", L"\x1100\x1161\x11ee"));
115     /* ㄱㅏㆁㆁㅏ */
116     fail_unless(check_commit("2y", "rkDDk", L"\x1100\x1161\x11f0"));
117     fail_unless(check_preedit("2y", "rkDDk", L"\x114c\x1161"));
118     /* ㅏㅏㅏㅏ */
119     fail_unless(check_preedit("2y", "kkkk", L"\x115f\x11a2"));
120 }
121 END_TEST
122 
START_TEST(test_hangul_ic_process_3f)123 START_TEST(test_hangul_ic_process_3f)
124 {
125     /* L V T  */
126     /* ㅎ     */
127     fail_unless(check_preedit("3f", "m", L"ㅎ"));
128     /*   ㅗ   */
129     fail_unless(check_preedit("3f", "v", L"ㅗ"));
130     /*     ㅌ */
131     fail_unless(check_preedit("3f", "W", L"ㅌ"));
132 
133     /* ㄱㅏㅇ */
134     fail_unless(check_preedit("3f", "kfa", L"강"));
135     /* ㄹㅐ   */
136     fail_unless(check_preedit("3f", "yr", L"래"));
137     /* ㄴ  ㅁ */
138     fail_unless(check_preedit("3f", "hz", L"\x1102\x1160\x11b7"));
139     /*   ㅜㅅ */
140     fail_unless(check_preedit("3f", "tq", L"\x115f\x1165\x11ba"));
141 }
142 END_TEST
143 
START_TEST(test_hangul_ic_process_romaja)144 START_TEST(test_hangul_ic_process_romaja)
145 {
146     HangulInputContext* ic;
147     const ucschar* preedit;
148     const ucschar* commit;
149 
150     // romaja keyboard test
151     ic = hangul_ic_new("ro");
152 
153     // basic test: han produces 한
154     hangul_ic_process(ic, 'h');
155     hangul_ic_process(ic, 'a');
156     hangul_ic_process(ic, 'n');
157 
158     preedit = hangul_ic_get_preedit_string(ic);
159     commit = hangul_ic_get_commit_string(ic);
160     fail_unless(preedit[0] == L'한');
161     fail_unless(commit[0] == 0);
162 
163     hangul_ic_reset(ic);
164 
165     // insert ㅇ when a syllable is not started with consonant
166     hangul_ic_process(ic, 'a');
167 
168     preedit = hangul_ic_get_preedit_string(ic);
169     commit = hangul_ic_get_commit_string(ic);
170     fail_unless(preedit[0] == L'아');
171     fail_unless(commit[0] == 0);
172 
173     // remove correctly when automatically ㅇ was inserted
174     hangul_ic_backspace(ic);
175 
176     preedit = hangul_ic_get_preedit_string(ic);
177     commit = hangul_ic_get_commit_string(ic);
178     fail_unless(preedit[0] == 0);
179     fail_unless(commit[0] == 0);
180 
181     // append ㅡ when a syllable is not ended with vowel
182     hangul_ic_process(ic, 't');
183     hangul_ic_process(ic, 't');
184 
185     preedit = hangul_ic_get_preedit_string(ic);
186     commit = hangul_ic_get_commit_string(ic);
187     fail_unless(preedit[0] == 0x314c); // ㅌ
188     fail_unless(commit[0] == L'트');
189 
190     // ng makes trailing ㅇ
191     hangul_ic_reset(ic);
192     hangul_ic_process(ic, 'g');
193     hangul_ic_process(ic, 'a');
194     hangul_ic_process(ic, 'n');
195     hangul_ic_process(ic, 'g');
196 
197     preedit = hangul_ic_get_preedit_string(ic);
198     commit = hangul_ic_get_commit_string(ic);
199     fail_unless(preedit[0] == L'강'); // 강
200     fail_unless(commit[0] == 0);
201 
202     // gangi makes 강이
203     hangul_ic_process(ic, 'i');
204 
205     preedit = hangul_ic_get_preedit_string(ic);
206     commit = hangul_ic_get_commit_string(ic);
207     fail_unless(preedit[0] == L'이');
208     fail_unless(commit[0] == L'강');  // 강
209 
210     // nanG makes 난ㄱ
211     // uppercase makes new syllable
212     hangul_ic_process(ic, 'n');
213     hangul_ic_process(ic, 'a');
214     hangul_ic_process(ic, 'n');
215     hangul_ic_process(ic, 'G');
216 
217     preedit = hangul_ic_get_preedit_string(ic);
218     commit = hangul_ic_get_commit_string(ic);
219     fail_unless(preedit[0] == 0x3131); // ㄱ
220     fail_unless(commit[0] == L'난');  // 난
221 
222     // special operation for x
223     // x generate ㅈ for leading consonant
224     hangul_ic_reset(ic);
225     hangul_ic_process(ic, 'x');
226     hangul_ic_process(ic, 'x');
227 
228     preedit = hangul_ic_get_preedit_string(ic);
229     commit = hangul_ic_get_commit_string(ic);
230     fail_unless(preedit[0] == 0x3148); // 지
231     fail_unless(commit[0] == L'즈');
232 
233     hangul_ic_reset(ic);
234     hangul_ic_process(ic, 'x');
235     hangul_ic_process(ic, 'y');
236 
237     preedit = hangul_ic_get_preedit_string(ic);
238     commit = hangul_ic_get_commit_string(ic);
239     fail_unless(preedit[0] == L'지'); // 지
240     fail_unless(commit[0] == 0x0);
241 
242     // x generate ㄱㅅ for trailing consonant
243     // and ㅅ will be transferred to next syllable when next input
244     // character is vowel.
245     hangul_ic_reset(ic);
246     hangul_ic_process(ic, 's');
247     hangul_ic_process(ic, 'e');
248     hangul_ic_process(ic, 'x');
249     hangul_ic_process(ic, 'y');
250 
251     preedit = hangul_ic_get_preedit_string(ic);
252     commit = hangul_ic_get_commit_string(ic);
253     fail_unless(preedit[0] == L'시'); // 시
254     fail_unless(commit[0] == L'섹');  // 섹
255 
256     hangul_ic_delete(ic);
257 }
258 END_TEST
259 
START_TEST(test_syllable_iterator)260 START_TEST(test_syllable_iterator)
261 {
262     ucschar str[] = {
263 	// L L V V T T
264 	0x1107, 0x1107, 0x116e, 0x1166, 0x11af, 0x11a8,          // 0
265 	// L V T
266 	0x1108, 0x1170, 0x11b0,                                  // 6
267 	// L L V V T T M
268 	0x1107, 0x1107, 0x116e, 0x1166, 0x11af, 0x11a8, 0x302E,  // 9
269 	// L V T M
270 	0x1108, 0x1170, 0x11b0, 0x302F,                          // 16
271 	// Lf V
272 	0x115f, 0x1161,                                          // 20
273 	// L Vf
274 	0x110c, 0x1160,                                          // 22
275 	// L LVT T
276 	0x1107, 0xbc14, 0x11a8,                                  // 24
277 	// L LV T
278 	0x1100, 0xac00, 0x11a8,                                  // 27
279 	// LVT
280 	0xc00d,                                                  // 30
281 	// other
282 	'a',                                                     // 31
283 	0                                                        // 32
284     };
285 
286     const ucschar* begin = str;
287     const ucschar* end = str + countof(str) - 1;
288     const ucschar* s = str;
289 
290     s = hangul_syllable_iterator_next(s, end);
291     fail_unless(s - str == 6,
292 		"error: next syllable: L L V V T T");
293 
294     s = hangul_syllable_iterator_next(s, end);
295     fail_unless(s - str == 9,
296 		"error: next syllable: L V T");
297 
298     s = hangul_syllable_iterator_next(s, end);
299     fail_unless(s - str == 16,
300 		"error: next syllable: L L V V T T M");
301 
302     s = hangul_syllable_iterator_next(s, end);
303     fail_unless(s - str == 20,
304 		"error: next syllable: L V T M");
305 
306     s = hangul_syllable_iterator_next(s, end);
307     fail_unless(s - str == 22,
308 		"error: next syllable: Lf V");
309 
310     s = hangul_syllable_iterator_next(s, end);
311     fail_unless(s - str == 24,
312 		"error: next syllable: L Vf");
313 
314     s = hangul_syllable_iterator_next(s, end);
315     fail_unless(s - str == 27,
316 		"error: next syllable: L LVT T");
317 
318     s = hangul_syllable_iterator_next(s, end);
319     fail_unless(s - str == 30,
320 		"error: next syllable: L LV T");
321 
322     s = hangul_syllable_iterator_next(s, end);
323     fail_unless(s - str == 31,
324 		"error: next syllable: LVT");
325 
326     s = hangul_syllable_iterator_next(s, end);
327     fail_unless(s - str == 32,
328 		"error: next syllable: other");
329 
330     s = end;
331     s = hangul_syllable_iterator_prev(s, begin);
332     fail_unless(s - str == 31,
333 		"error: prev syllable: other");
334 
335     s = hangul_syllable_iterator_prev(s, begin);
336     fail_unless(s - str == 30,
337 		"error: prev syllable: LVT");
338 
339     s = hangul_syllable_iterator_prev(s, begin);
340     fail_unless(s - str == 27,
341 		"error: prev syllable: L LV T");
342 
343     s = hangul_syllable_iterator_prev(s, begin);
344     fail_unless(s - str == 24,
345 		"error: prev syllable: L LVT T");
346 
347     s = hangul_syllable_iterator_prev(s, begin);
348     fail_unless(s - str == 22,
349 		"error: prev syllable: L Vf");
350 
351     s = hangul_syllable_iterator_prev(s, begin);
352     fail_unless(s - str == 20,
353 		"error: prev syllable: Lf V");
354 
355     s = hangul_syllable_iterator_prev(s, begin);
356     fail_unless(s - str == 16,
357 		"error: prev syllable: L V T M");
358 
359     s = hangul_syllable_iterator_prev(s, begin);
360     fail_unless(s - str == 9,
361 		"error: prev syllable: L L V V T T M");
362 
363     s = hangul_syllable_iterator_prev(s, begin);
364     fail_unless(s - str == 6,
365 		"error: prev syllable: L V T");
366 
367     s = hangul_syllable_iterator_prev(s, begin);
368     fail_unless(s - str == 0,
369 		"error: prev syllable: L L V V T T");
370 }
371 END_TEST
372 
START_TEST(test_hangul_keyboard)373 START_TEST(test_hangul_keyboard)
374 {
375     const char* id;
376     const char* name;
377     unsigned int n;
378     unsigned int i;
379 
380     n = hangul_ic_get_n_keyboards();
381     fail_unless(n != 0,
382 		"error: there is no builtin hangul keyboard");
383 
384     for (i = 0; i < n; ++i) {
385 	id = hangul_ic_get_keyboard_id(i);
386 	fail_unless(id != NULL,
387 		    "error: keyboard id == NULL");
388     }
389 
390     for (i = 0; i < n; ++i) {
391 	name = hangul_ic_get_keyboard_name(i);
392 	fail_unless(name != NULL,
393 		    "error: keyboard id == NULL");
394     }
395 }
396 END_TEST
397 
libhangul_suite()398 Suite* libhangul_suite()
399 {
400     Suite* s = suite_create("libhangul");
401 
402     TCase* hangul = tcase_create("hangul");
403     tcase_add_test(hangul, test_hangul_ic_process_2);
404     tcase_add_test(hangul, test_hangul_ic_process_2y);
405     tcase_add_test(hangul, test_hangul_ic_process_3f);
406     tcase_add_test(hangul, test_hangul_ic_process_romaja);
407     tcase_add_test(hangul, test_syllable_iterator);
408     tcase_add_test(hangul, test_hangul_keyboard);
409     suite_add_tcase(s, hangul);
410 
411     return s;
412 }
413 
main()414 int main()
415 {
416     int number_failed;
417     Suite* s = libhangul_suite();
418     SRunner* sr = srunner_create(s);
419 
420     srunner_set_fork_status(sr, CK_NOFORK);
421     srunner_run_all(sr, CK_NORMAL);
422 
423     number_failed = srunner_ntests_failed(sr);
424     srunner_free(sr);
425 
426     return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
427 }
428