1 /*
2  *	Example program for the Allegro library.
3  *
4  *	Test UTF-8 string routines.
5  */
6 
7 /* TODO: we should also be checking on inputs with surrogate characters
8  * (which are not allowed)
9  */
10 
11 #include <allegro5/allegro.h>
12 #include <allegro5/utf8.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 
16 #include "common.c"
17 
18 #ifdef ALLEGRO_MSVC
19    #pragma warning (disable: 4066)
20 #endif
21 
22 /* Some unicode characters */
23 #define U_ae         0x00e6   /* æ */
24 #define U_i_acute    0x00ed   /* í */
25 #define U_eth        0x00f0   /* ð */
26 #define U_o_dia      0x00f6   /* ö */
27 #define U_thorn      0x00fe   /* þ */
28 #define U_z_bar      0x01b6   /* ƶ */
29 #define U_schwa      0x0259   /* ə */
30 #define U_beta       0x03b2   /* β */
31 #define U_1d08       0x1d08   /* ᴈ */
32 #define U_1ff7       0x1ff7   /* ῷ */
33 #define U_2051       0x2051   /* ⁑ */
34 #define U_euro       0x20ac   /* € */
35 
36 typedef void (*test_t)(void);
37 
38 int error = 0;
39 
40 #define CHECK(x)                                                            \
41    do {                                                                     \
42       bool ok = (x);                                                        \
43       if (!ok) {                                                            \
44          log_printf("FAIL %s\n", #x);                                       \
45          error++;                                                           \
46       } else {                                                              \
47          log_printf("OK   %s\n", #x);                                       \
48       }                                                                     \
49    } while (0)
50 
51 /* Test that we can create and destroy strings and get their data and size. */
t1(void)52 static void t1(void)
53 {
54    ALLEGRO_USTR *us1 = al_ustr_new("");
55    ALLEGRO_USTR *us2 = al_ustr_new("áƵ");
56 
57    CHECK(0 == strcmp(al_cstr(us1), ""));
58    CHECK(0 == strcmp(al_cstr(us2), "áƵ"));
59    CHECK(4 == al_ustr_size(us2));
60 
61    al_ustr_free(us1);
62    al_ustr_free(us2);
63 }
64 
t2(void)65 static void t2(void)
66 {
67    CHECK(0 == al_ustr_size(al_ustr_empty_string()));
68    CHECK(0 == strcmp(al_cstr(al_ustr_empty_string()), ""));
69 }
70 
71 /* Test that we make strings which reference other C strings. */
72 /* No memory needs to be freed. */
t3(void)73 static void t3(void)
74 {
75    ALLEGRO_USTR_INFO info;
76    const ALLEGRO_USTR *us = al_ref_cstr(&info, "A static string.");
77 
78    CHECK(0 == strcmp(al_cstr(us), "A static string."));
79 }
80 
81 /* Test that we can make strings which reference arbitrary memory blocks. */
82 /* No memory needs to be freed. */
t4(void)83 static void t4(void)
84 {
85    const char s[] = "This contains an embedded NUL: \0 <-- here";
86    ALLEGRO_USTR_INFO info;
87    const ALLEGRO_USTR *us = al_ref_buffer(&info, s, sizeof(s));
88 
89    CHECK(al_ustr_size(us) == sizeof(s));
90    CHECK(0 == memcmp(al_cstr(us), s, sizeof(s)));
91 }
92 
93 /* Test that we can make strings which reference (parts of) other strings. */
t5(void)94 static void t5(void)
95 {
96    ALLEGRO_USTR *us1;
97    const ALLEGRO_USTR *us2;
98    ALLEGRO_USTR_INFO us2_info;
99 
100    us1 = al_ustr_new("aábdðeéfghiíjklmnoóprstuúvxyýþæö");
101 
102    us2 = al_ref_ustr(&us2_info, us1, 36, 36 + 4);
103    CHECK(0 == memcmp(al_cstr(us2), "þæ", al_ustr_size(us2)));
104 
105    /* Start pos underflow */
106    us2 = al_ref_ustr(&us2_info, us1, -10, 7);
107    CHECK(0 == memcmp(al_cstr(us2), "aábdð", al_ustr_size(us2)));
108 
109    /* End pos overflow */
110    us2 = al_ref_ustr(&us2_info, us1, 36, INT_MAX);
111    CHECK(0 == memcmp(al_cstr(us2), "þæö", al_ustr_size(us2)));
112 
113    /* Start > end */
114    us2 = al_ref_ustr(&us2_info, us1, 36 + 4, 36);
115    CHECK(0 == al_ustr_size(us2));
116 
117    al_ustr_free(us1);
118 }
119 
120 /* Test al_ustr_dup. */
t6(void)121 static void t6(void)
122 {
123    ALLEGRO_USTR *us1 = al_ustr_new("aábdðeéfghiíjklmnoóprstuúvxyýþæö");
124    ALLEGRO_USTR *us2 = al_ustr_dup(us1);
125 
126    CHECK(al_ustr_size(us1) == al_ustr_size(us2));
127    CHECK(0 == memcmp(al_cstr(us1), al_cstr(us2), al_ustr_size(us1)));
128 
129    al_ustr_free(us1);
130    al_ustr_free(us2);
131 }
132 
133 /* Test al_ustr_dup_substr. */
t7(void)134 static void t7(void)
135 {
136    ALLEGRO_USTR *us1;
137    ALLEGRO_USTR *us2;
138 
139    us1 = al_ustr_new("aábdðeéfghiíjklmnoóprstuúvxyýþæö");
140 
141    /* Cut out part of a string.  Check for NUL terminator. */
142    us2 = al_ustr_dup_substr(us1, 36, 36 + 4);
143    CHECK(al_ustr_size(us2) == 4);
144    CHECK(0 == strcmp(al_cstr(us2), "þæ"));
145    al_ustr_free(us2);
146 
147    /* Under and overflow */
148    us2 = al_ustr_dup_substr(us1, INT_MIN, INT_MAX);
149    CHECK(al_ustr_size(us2) == al_ustr_size(us1));
150    CHECK(0 == strcmp(al_cstr(us2), al_cstr(us1)));
151    al_ustr_free(us2);
152 
153    /* Start > end */
154    us2 = al_ustr_dup_substr(us1, INT_MAX, INT_MIN);
155    CHECK(0 == al_ustr_size(us2));
156    al_ustr_free(us2);
157 
158    al_ustr_free(us1);
159 }
160 
161 /* Test al_ustr_append, al_ustr_append_cstr. */
t8(void)162 static void t8(void)
163 {
164    ALLEGRO_USTR *us1 = al_ustr_new("aábdðeéfghiíjklm");
165    ALLEGRO_USTR *us2 = al_ustr_new("noóprstuú");
166 
167    CHECK(al_ustr_append(us1, us2));
168    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjklmnoóprstuú"));
169 
170    CHECK(al_ustr_append_cstr(us1, "vxyýþæö"));
171    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjklmnoóprstuúvxyýþæö"));
172 
173    al_ustr_free(us1);
174    al_ustr_free(us2);
175 }
176 
177 /* Test al_ustr_append with aliased strings. */
t9(void)178 static void t9(void)
179 {
180    ALLEGRO_USTR *us1;
181    ALLEGRO_USTR_INFO us2_info;
182    const ALLEGRO_USTR *us2;
183 
184    /* Append a string to itself. */
185    us1 = al_ustr_new("aábdðeéfghiíjklm");
186    CHECK(al_ustr_append(us1, us1));
187    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjklmaábdðeéfghiíjklm"));
188    al_ustr_free(us1);
189 
190    /* Append a substring of a string to itself. */
191    us1 = al_ustr_new("aábdðeéfghiíjklm");
192    us2 = al_ref_ustr(&us2_info, us1, 5, 5 + 11); /* ð...í */
193    CHECK(al_ustr_append(us1, us2));
194    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjklmðeéfghií"));
195    al_ustr_free(us1);
196 }
197 
198 /* Test al_ustr_equal. */
t10(void)199 static void t10(void)
200 {
201    ALLEGRO_USTR *us1;
202    ALLEGRO_USTR *us2;
203    const ALLEGRO_USTR *us3;
204    ALLEGRO_USTR_INFO us3_info;
205    const char us3_data[] = "aábdð\0eéfgh";
206 
207    us1 = al_ustr_new("aábdð");
208    us2 = al_ustr_dup(us1);
209    us3 = al_ref_buffer(&us3_info, us3_data, sizeof(us3_data));
210 
211    CHECK(al_ustr_equal(us1, us2));
212    CHECK(!al_ustr_equal(us1, al_ustr_empty_string()));
213 
214    /* Check comparison doesn't stop at embedded NUL. */
215    CHECK(!al_ustr_equal(us1, us3));
216 
217    al_ustr_free(us1);
218    al_ustr_free(us2);
219 }
220 
221 /* Test al_ustr_insert. */
t11(void)222 static void t11(void)
223 {
224    ALLEGRO_USTR *us1;
225    ALLEGRO_USTR *us2;
226    size_t sz;
227 
228    /* Insert in middle. */
229    us1 = al_ustr_new("aábdðeéfghiíjkprstuúvxyýþæö");
230    us2 = al_ustr_new("lmnoó");
231    al_ustr_insert(us1, 18, us2);
232    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjklmnoóprstuúvxyýþæö"));
233 
234    /* Insert into itself. */
235    al_ustr_insert(us2, 3, us2);
236    CHECK(0 == strcmp(al_cstr(us2), "lmnlmnoóoó"));
237 
238    /* Insert before start (not allowed). */
239    CHECK(!al_ustr_insert(us2, -1, us2));
240 
241    /* Insert past end (will be padded with NULs). */
242    sz = al_ustr_size(us2);
243    al_ustr_insert(us2, sz + 3, us2);
244    CHECK(al_ustr_size(us2) == sz + sz + 3);
245    CHECK(0 == memcmp(al_cstr(us2), "lmnlmnoóoó\0\0\0lmnlmnoóoó", sz + sz + 3));
246 
247    al_ustr_free(us1);
248    al_ustr_free(us2);
249 }
250 
251 /* Test al_ustr_insert_cstr. */
t12(void)252 static void t12(void)
253 {
254    ALLEGRO_USTR *us1 = al_ustr_new("aábeéf");
255    CHECK(al_ustr_insert_cstr(us1, 4, "dð"));
256    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéf"));
257    al_ustr_free(us1);
258 }
259 
260 /* Test al_ustr_remove_range. */
t13(void)261 static void t13(void)
262 {
263    ALLEGRO_USTR *us1 = al_ustr_new("aábdðeéfghiíjkprstuúvxyýþæö");
264 
265    /* Remove from middle of string. */
266    CHECK(al_ustr_remove_range(us1, 5, 30));
267    CHECK(0 == strcmp(al_cstr(us1), "aábdþæö"));
268 
269    /* Removing past end. */
270    CHECK(al_ustr_remove_range(us1, 100, 120));
271    CHECK(0 == strcmp(al_cstr(us1), "aábdþæö"));
272 
273    /* Start > End. */
274    CHECK(! al_ustr_remove_range(us1, 3, 0));
275    CHECK(0 == strcmp(al_cstr(us1), "aábdþæö"));
276 
277    al_ustr_free(us1);
278 }
279 
280 /* Test al_ustr_truncate. */
t14(void)281 static void t14(void)
282 {
283    ALLEGRO_USTR *us1 = al_ustr_new("aábdðeéfghiíjkprstuúvxyýþæö");
284 
285    /* Truncate from middle of string. */
286    CHECK(al_ustr_truncate(us1, 30));
287    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjkprstuúvxyý"));
288 
289    /* Truncate past end (allowed). */
290    CHECK(al_ustr_truncate(us1, 100));
291    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjkprstuúvxyý"));
292 
293    /* Truncate before start (not allowed). */
294    CHECK(! al_ustr_truncate(us1, -1));
295    CHECK(0 == strcmp(al_cstr(us1), "aábdðeéfghiíjkprstuúvxyý"));
296 
297    al_ustr_free(us1);
298 }
299 
300 /* Test whitespace trim functions. */
t15(void)301 static void t15(void)
302 {
303    ALLEGRO_USTR *us1 = al_ustr_new(" \f\n\r\t\vhello \f\n\r\t\v");
304    ALLEGRO_USTR *us2 = al_ustr_new(" \f\n\r\t\vhello \f\n\r\t\v");
305 
306    CHECK(al_ustr_ltrim_ws(us1));
307    CHECK(0 == strcmp(al_cstr(us1), "hello \f\n\r\t\v"));
308 
309    CHECK(al_ustr_rtrim_ws(us1));
310    CHECK(0 == strcmp(al_cstr(us1), "hello"));
311 
312    CHECK(al_ustr_trim_ws(us2));
313    CHECK(0 == strcmp(al_cstr(us2), "hello"));
314 
315    al_ustr_free(us1);
316    al_ustr_free(us2);
317 }
318 
319 /* Test whitespace trim functions (edge cases). */
t16(void)320 static void t16(void)
321 {
322    ALLEGRO_USTR *us1;
323 
324    /* Check return value when passed empty strings. */
325    us1 = al_ustr_new("");
326    CHECK(al_ustr_ltrim_ws(us1));
327    CHECK(al_ustr_rtrim_ws(us1));
328    CHECK(al_ustr_trim_ws(us1));
329    al_ustr_free(us1);
330 
331    /* Check nothing bad happens if the whole string is whitespace. */
332    us1 = al_ustr_new(" \f\n\r\t\v");
333    CHECK(al_ustr_ltrim_ws(us1));
334    CHECK(al_ustr_size(us1) == 0);
335    al_ustr_free(us1);
336 
337    us1 = al_ustr_new(" \f\n\r\t\v");
338    CHECK(al_ustr_rtrim_ws(us1));
339    CHECK(al_ustr_size(us1) == 0);
340    al_ustr_free(us1);
341 
342    us1 = al_ustr_new(" \f\n\r\t\v");
343    CHECK(al_ustr_trim_ws(us1));
344    CHECK(al_ustr_size(us1) == 0);
345    al_ustr_free(us1);
346 }
347 
348 /* Test al_utf8_width. */
t17(void)349 static void t17(void)
350 {
351    CHECK(al_utf8_width(0x000000) == 1);
352    CHECK(al_utf8_width(0x00007f) == 1);
353    CHECK(al_utf8_width(0x000080) == 2);
354    CHECK(al_utf8_width(0x0007ff) == 2);
355    CHECK(al_utf8_width(0x000800) == 3);
356    CHECK(al_utf8_width(0x00ffff) == 3);
357    CHECK(al_utf8_width(0x010000) == 4);
358    CHECK(al_utf8_width(0x10ffff) == 4);
359 
360    /* These are illegal. */
361    CHECK(al_utf8_width(0x110000) == 0);
362    CHECK(al_utf8_width(0xffffff) == 0);
363 }
364 
365 /* Test al_utf8_encode. */
t18(void)366 static void t18(void)
367 {
368    char buf[4];
369 
370    CHECK(al_utf8_encode(buf, 0) == 1);
371    CHECK(0 == memcmp(buf, "\x00", 1));
372 
373    CHECK(al_utf8_encode(buf, 0x7f) == 1);
374    CHECK(0 == memcmp(buf, "\x7f", 1));
375 
376    CHECK(al_utf8_encode(buf, 0x80) == 2);
377    CHECK(0 == memcmp(buf, "\xC2\x80", 2));
378 
379    CHECK(al_utf8_encode(buf, 0x7ff) == 2);
380    CHECK(0 == memcmp(buf, "\xDF\xBF", 2));
381 
382    CHECK(al_utf8_encode(buf, 0x000800) == 3);
383    CHECK(0 == memcmp(buf, "\xE0\xA0\x80", 3));
384 
385    CHECK(al_utf8_encode(buf, 0x00ffff) == 3);
386    CHECK(0 == memcmp(buf, "\xEF\xBF\xBF", 3));
387 
388    CHECK(al_utf8_encode(buf, 0x010000) == 4);
389    CHECK(0 == memcmp(buf, "\xF0\x90\x80\x80", 4));
390 
391    CHECK(al_utf8_encode(buf, 0x10ffff) == 4);
392    CHECK(0 == memcmp(buf, "\xF4\x8f\xBF\xBF", 4));
393 
394    /* These are illegal. */
395    CHECK(al_utf8_encode(buf, 0x110000) == 0);
396    CHECK(al_utf8_encode(buf, 0xffffff) == 0);
397 }
398 
399 /* Test al_ustr_insert_chr. */
t19(void)400 static void t19(void)
401 {
402    ALLEGRO_USTR *us = al_ustr_new("");
403 
404    CHECK(al_ustr_insert_chr(us, 0, 'a') == 1);
405    CHECK(al_ustr_insert_chr(us, 0, U_ae) == 2);
406    CHECK(al_ustr_insert_chr(us, 2, U_euro) == 3);
407    CHECK(0 == strcmp(al_cstr(us), "æ€a"));
408 
409    /* Past end. */
410    CHECK(al_ustr_insert_chr(us, 8, U_o_dia) == 2);
411    CHECK(0 == memcmp(al_cstr(us), "æ€a\0\0ö", 9));
412 
413    /* Invalid code points. */
414    CHECK(al_ustr_insert_chr(us, 0, -1) == 0);
415    CHECK(al_ustr_insert_chr(us, 0, 0x110000) == 0);
416 
417    al_ustr_free(us);
418 }
419 
420 /* Test al_ustr_append_chr. */
t20(void)421 static void t20(void)
422 {
423    ALLEGRO_USTR *us = al_ustr_new("");
424 
425    CHECK(al_ustr_append_chr(us, 'a') == 1);
426    CHECK(al_ustr_append_chr(us, U_ae) == 2);
427    CHECK(al_ustr_append_chr(us, U_euro) == 3);
428    CHECK(0 == strcmp(al_cstr(us), "aæ€"));
429 
430    /* Invalid code points. */
431    CHECK(al_ustr_append_chr(us, -1) == 0);
432    CHECK(al_ustr_append_chr(us, 0x110000) == 0);
433 
434    al_ustr_free(us);
435 }
436 
437 /* Test al_ustr_get. */
t21(void)438 static void t21(void)
439 {
440    ALLEGRO_USTR_INFO info;
441    const ALLEGRO_USTR *us;
442 
443    us = al_ref_buffer(&info, "", 1);
444    CHECK(al_ustr_get(us, 0) == 0);
445 
446    us = al_ref_cstr(&info, "\x7f");
447    CHECK(al_ustr_get(us, 0) == 0x7f);
448 
449    us = al_ref_cstr(&info, "\xC2\x80");
450    CHECK(al_ustr_get(us, 0) == 0x80);
451 
452    us = al_ref_cstr(&info, "\xDF\xBf");
453    CHECK(al_ustr_get(us, 0) == 0x7ff);
454 
455    us = al_ref_cstr(&info, "\xE0\xA0\x80");
456    CHECK(al_ustr_get(us, 0) == 0x800);
457 
458    us = al_ref_cstr(&info, "\xEF\xBF\xBF");
459    CHECK(al_ustr_get(us, 0) == 0xffff);
460 
461    us = al_ref_cstr(&info, "\xF0\x90\x80\x80");
462    CHECK(al_ustr_get(us, 0) == 0x010000);
463 
464    us = al_ref_cstr(&info, "\xF4\x8F\xBF\xBF");
465    CHECK(al_ustr_get(us, 0) == 0x10ffff);
466 }
467 
468 /* Test al_ustr_get on invalid sequences. */
t22(void)469 static void t22(void)
470 {
471    ALLEGRO_USTR_INFO info;
472    const ALLEGRO_USTR *us;
473 
474    /* Empty string. */
475    al_set_errno(0);
476    CHECK(al_ustr_get(al_ustr_empty_string(), 0) < 0);
477    CHECK(al_get_errno() == ERANGE);
478 
479    /* 5-byte sequence. */
480    us = al_ref_cstr(&info, "\xf8\x88\x80\x80\x80");
481    al_set_errno(0);
482    CHECK(al_ustr_get(us, 0) < 0);
483    CHECK(al_get_errno() == EILSEQ);
484 
485    /* Start in trail byte. */
486    us = al_ref_cstr(&info, "ð");
487    al_set_errno(0);
488    CHECK(al_ustr_get(us, 1) < 0);
489    CHECK(al_get_errno() == EILSEQ);
490 
491    /* Truncated 3-byte sequence. */
492    us = al_ref_cstr(&info, "\xEF\xBF");
493    al_set_errno(0);
494    CHECK(al_ustr_get(us, 0) < 0);
495    CHECK(al_get_errno() == EILSEQ);
496 }
497 
498 /* Test al_ustr_get on invalid sequences (part 2). */
499 /* Get more ideas for tests from
500  * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
501  */
t23(void)502 static void t23(void)
503 {
504    ALLEGRO_USTR_INFO info;
505    const ALLEGRO_USTR *us;
506 
507    /* Examples of an overlong ASCII character */
508    us = al_ref_cstr(&info, "\xc0\xaf");
509    CHECK(al_ustr_get(us, 0) < 0);
510    us = al_ref_cstr(&info, "\xe0\x80\xaf");
511    CHECK(al_ustr_get(us, 0) < 0);
512    us = al_ref_cstr(&info, "\xf0\x80\x80\xaf");
513    CHECK(al_ustr_get(us, 0) < 0);
514    us = al_ref_cstr(&info, "\xf8\x80\x80\x80\xaf");
515    CHECK(al_ustr_get(us, 0) < 0);
516    us = al_ref_cstr(&info, "\xfc\x80\x80\x80\x80\xaf");
517    CHECK(al_ustr_get(us, 0) < 0);
518 
519    /* Maximum overlong sequences */
520    us = al_ref_cstr(&info, "\xc1\xbf");
521    CHECK(al_ustr_get(us, 0) < 0);
522 
523    us = al_ref_cstr(&info, "\xe0\x9f\xbf");
524    CHECK(al_ustr_get(us, 0) < 0);
525 
526    us = al_ref_cstr(&info, "\xf0\x8f\xbf\xbf");
527    CHECK(al_ustr_get(us, 0) < 0);
528 
529    us = al_ref_cstr(&info, "\xf8\x87\xbf\xbf\xbf");
530    CHECK(al_ustr_get(us, 0) < 0);
531 
532    us = al_ref_cstr(&info, "\xfc\x83\xbf\xbf\xbf\xbf");
533    CHECK(al_ustr_get(us, 0) < 0);
534 
535    /* Overlong representation of the NUL character */
536    us = al_ref_cstr(&info, "\xc0\x80");
537    CHECK(al_ustr_get(us, 0) < 0);
538 
539    us = al_ref_cstr(&info, "\xe0\x80\x80");
540    CHECK(al_ustr_get(us, 0) < 0);
541 
542    us = al_ref_cstr(&info, "\xf0\x80\x80");
543    CHECK(al_ustr_get(us, 0) < 0);
544 
545    us = al_ref_cstr(&info, "\xf8\x80\x80\x80");
546    CHECK(al_ustr_get(us, 0) < 0);
547 
548    us = al_ref_cstr(&info, "\xfc\x80\x80\x80\x80");
549    CHECK(al_ustr_get(us, 0) < 0);
550 }
551 
552 /* Test al_ustr_next. */
t24(void)553 static void t24(void)
554 {
555    const char str[] = "a\0þ€\xf4\x8f\xbf\xbf";
556    ALLEGRO_USTR_INFO info;
557    const ALLEGRO_USTR *us = al_ref_buffer(&info, str, sizeof(str) - 1);
558    int pos = 0;
559 
560    CHECK(al_ustr_next(us, &pos));   /* a */
561    CHECK(pos == 1);
562 
563    CHECK(al_ustr_next(us, &pos));   /* NUL */
564    CHECK(pos == 2);
565 
566    CHECK(al_ustr_next(us, &pos));   /* þ */
567    CHECK(pos == 4);
568 
569    CHECK(al_ustr_next(us, &pos));   /* € */
570    CHECK(pos == 7);
571 
572    CHECK(al_ustr_next(us, &pos));   /* U+10FFFF */
573    CHECK(pos == 11);
574 
575    CHECK(! al_ustr_next(us, &pos)); /* end */
576    CHECK(pos == 11);
577 }
578 
579 /* Test al_ustr_next with invalid input. */
t25(void)580 static void t25(void)
581 {
582    const char str[] = "þ\xf4\x8f\xbf.";
583    ALLEGRO_USTR_INFO info;
584    const ALLEGRO_USTR *us = al_ref_buffer(&info, str, sizeof(str) - 1);
585    int pos;
586 
587    /* Starting in middle of a sequence. */
588    pos = 1;
589    CHECK(al_ustr_next(us, &pos));
590    CHECK(pos == 2);
591 
592    /* Unexpected end of 4-byte sequence. */
593    CHECK(al_ustr_next(us, &pos));
594    CHECK(pos == 5);
595 }
596 
597 /* Test al_ustr_prev. */
t26(void)598 static void t26(void)
599 {
600    ALLEGRO_USTR *us = al_ustr_new("aþ€\xf4\x8f\xbf\xbf");
601    int pos = al_ustr_size(us);
602 
603    CHECK(al_ustr_prev(us, &pos));   /* U+10FFFF */
604    CHECK(pos == 6);
605 
606    CHECK(al_ustr_prev(us, &pos));   /* € */
607    CHECK(pos == 3);
608 
609    CHECK(al_ustr_prev(us, &pos));   /* þ */
610    CHECK(pos == 1);
611 
612    CHECK(al_ustr_prev(us, &pos));   /* a */
613    CHECK(pos == 0);
614 
615    CHECK(!al_ustr_prev(us, &pos));  /* begin */
616    CHECK(pos == 0);
617 
618    al_ustr_free(us);
619 }
620 
621 /* Test al_ustr_length. */
t27(void)622 static void t27(void)
623 {
624    ALLEGRO_USTR *us = al_ustr_new("aþ€\xf4\x8f\xbf\xbf");
625 
626    CHECK(0 == al_ustr_length(al_ustr_empty_string()));
627    CHECK(4 == al_ustr_length(us));
628 
629    al_ustr_free(us);
630 }
631 
632 /* Test al_ustr_offset. */
t28(void)633 static void t28(void)
634 {
635    ALLEGRO_USTR *us = al_ustr_new("aþ€\xf4\x8f\xbf\xbf");
636 
637    CHECK(al_ustr_offset(us, 0) == 0);
638    CHECK(al_ustr_offset(us, 1) == 1);
639    CHECK(al_ustr_offset(us, 2) == 3);
640    CHECK(al_ustr_offset(us, 3) == 6);
641    CHECK(al_ustr_offset(us, 4) == 10);
642    CHECK(al_ustr_offset(us, 5) == 10);
643 
644    CHECK(al_ustr_offset(us, -1) == 6);
645    CHECK(al_ustr_offset(us, -2) == 3);
646    CHECK(al_ustr_offset(us, -3) == 1);
647    CHECK(al_ustr_offset(us, -4) == 0);
648    CHECK(al_ustr_offset(us, -5) == 0);
649 
650    al_ustr_free(us);
651 }
652 
653 /* Test al_ustr_get_next. */
t29(void)654 static void t29(void)
655 {
656    ALLEGRO_USTR *us = al_ustr_new("aþ€");
657    int pos;
658 
659    pos = 0;
660    CHECK(al_ustr_get_next(us, &pos) == 'a');
661    CHECK(al_ustr_get_next(us, &pos) == U_thorn);
662    CHECK(al_ustr_get_next(us, &pos) == U_euro);
663    CHECK(al_ustr_get_next(us, &pos) == -1);
664    CHECK(pos == (int) al_ustr_size(us));
665 
666    /* Start in the middle of þ. */
667    pos = 2;
668    CHECK(al_ustr_get_next(us, &pos) == -2);
669    CHECK(al_ustr_get_next(us, &pos) == U_euro);
670 
671    al_ustr_free(us);
672 }
673 
674 /* Test al_ustr_prev_get. */
t30(void)675 static void t30(void)
676 {
677    ALLEGRO_USTR *us = al_ustr_new("aþ€");
678    int pos;
679 
680    pos = al_ustr_size(us);
681    CHECK(al_ustr_prev_get(us, &pos) == U_euro);
682    CHECK(al_ustr_prev_get(us, &pos) == U_thorn);
683    CHECK(al_ustr_prev_get(us, &pos) == 'a');
684    CHECK(al_ustr_prev_get(us, &pos) == -1);
685 
686    /* Start in the middle of þ. */
687    pos = 2;
688    CHECK(al_ustr_prev_get(us, &pos) == U_thorn);
689 
690    al_ustr_free(us);
691 }
692 
693 /* Test al_ustr_find_chr. */
t31(void)694 static void t31(void)
695 {
696    ALLEGRO_USTR *us = al_ustr_new("aábdðeéfghiíaábdðeéfghií");
697 
698    /* Find ASCII. */
699    CHECK(al_ustr_find_chr(us, 0, 'e') == 7);
700    CHECK(al_ustr_find_chr(us, 7, 'e') == 7);    /* start_pos is inclusive */
701    CHECK(al_ustr_find_chr(us, 8, 'e') == 23);
702    CHECK(al_ustr_find_chr(us, 0, '.') == -1);
703 
704    /* Find non-ASCII. */
705    CHECK(al_ustr_find_chr(us, 0, U_eth) == 5);
706    CHECK(al_ustr_find_chr(us, 5, U_eth) == 5);   /* start_pos is inclusive */
707    CHECK(al_ustr_find_chr(us, 6, U_eth) == 21);
708    CHECK(al_ustr_find_chr(us, 0, U_z_bar) == -1);
709 
710    al_ustr_free(us);
711 }
712 
713 /* Test al_ustr_rfind_chr. */
t32(void)714 static void t32(void)
715 {
716    ALLEGRO_USTR *us = al_ustr_new("aábdðeéfghiíaábdðeéfghií");
717    int end = al_ustr_size(us);
718 
719    /* Find ASCII. */
720    CHECK(al_ustr_rfind_chr(us, end, 'e') == 23);
721    CHECK(al_ustr_rfind_chr(us, 23, 'e') == 7);        /* end_pos exclusive */
722    CHECK(al_ustr_rfind_chr(us, end, '.') == -1);
723 
724    /* Find non-ASCII. */
725    CHECK(al_ustr_rfind_chr(us, end, U_i_acute) == 30);
726    CHECK(al_ustr_rfind_chr(us, end - 1, U_i_acute) == 14); /* end_pos exclusive */
727    CHECK(al_ustr_rfind_chr(us, end, U_z_bar) == -1);
728 
729    al_ustr_free(us);
730 }
731 
732 /* Test al_ustr_find_set, al_ustr_find_set_cstr. */
t33(void)733 static void t33(void)
734 {
735    ALLEGRO_USTR *us = al_ustr_new("aábdðeéfghiíaábdðeéfghií");
736 
737    /* al_ustr_find_set_cstr is s simple wrapper for al_ustr_find_set
738     * so we test using that.
739     */
740 
741    /* Find ASCII. */
742    CHECK(al_ustr_find_set_cstr(us, 0, "gfe") == 7);
743    CHECK(al_ustr_find_set_cstr(us, 7, "gfe") == 7);  /* start_pos inclusive */
744    CHECK(al_ustr_find_set_cstr(us, 0, "") == -1);
745    CHECK(al_ustr_find_set_cstr(us, 0, "xyz") == -1);
746 
747    /* Find non-ASCII. */
748    CHECK(al_ustr_find_set_cstr(us, 0, "éðf") == 5);
749    CHECK(al_ustr_find_set_cstr(us, 5, "éðf") == 5);   /* start_pos inclusive */
750    CHECK(al_ustr_find_set_cstr(us, 0, "ẋỹƶ") == -1);
751 
752    al_ustr_free(us);
753 }
754 
755 /* Test al_ustr_find_set, al_ustr_find_set_cstr (invalid values).  */
t34(void)756 static void t34(void)
757 {
758    ALLEGRO_USTR *us = al_ustr_new("a\x80ábdðeéfghií");
759 
760    /* Invalid byte sequence in search string. */
761    CHECK(al_ustr_find_set_cstr(us, 0, "gfe") == 8);
762 
763    /* Invalid byte sequence in accept set. */
764    CHECK(al_ustr_find_set_cstr(us, 0, "é\x80ðf") == 6);
765 
766    al_ustr_free(us);
767 }
768 
769 /* Test al_ustr_find_cset, al_ustr_find_cset_cstr. */
t35(void)770 static void t35(void)
771 {
772    ALLEGRO_USTR *us;
773 
774    /* al_ustr_find_cset_cstr is s simple wrapper for al_ustr_find_cset
775     * so we test using that.
776     */
777 
778    /* Find ASCII. */
779    us = al_ustr_new("alphabetagamma");
780    CHECK(al_ustr_find_cset_cstr(us, 0, "alphbet") == 9);
781    CHECK(al_ustr_find_cset_cstr(us, 9, "alphbet") == 9);
782    CHECK(al_ustr_find_cset_cstr(us, 0, "") == -1);
783    CHECK(al_ustr_find_cset_cstr(us, 0, "alphbetgm") == -1);
784    al_ustr_free(us);
785 
786    /* Find non-ASCII. */
787    us = al_ustr_new("αλφαβεταγαμμα");
788    CHECK(al_ustr_find_cset_cstr(us, 0, "αλφβετ") == 16);
789    CHECK(al_ustr_find_cset_cstr(us, 16, "αλφβετ") == 16);
790    CHECK(al_ustr_find_cset_cstr(us, 0, "αλφβετγμ") == -1);
791    al_ustr_free(us);
792 }
793 
794 /* Test al_ustr_find_cset, al_ustr_find_set_cstr (invalid values).  */
t36(void)795 static void t36(void)
796 {
797    ALLEGRO_USTR *us = al_ustr_new("a\x80ábdðeéfghií");
798 
799    /* Invalid byte sequence in search string. */
800    CHECK(al_ustr_find_cset_cstr(us, 0, "aábd") == 6);
801 
802    /* Invalid byte sequence in reject set. */
803    CHECK(al_ustr_find_cset_cstr(us, 0, "a\x80ábd") == 6);
804 
805    al_ustr_free(us);
806 }
807 
808 /* Test al_ustr_find_str, al_ustr_find_cstr. */
t37(void)809 static void t37(void)
810 {
811    ALLEGRO_USTR *us = al_ustr_new("aábdðeéfghiíaábdðeéfghií");
812 
813    /* al_ustr_find_cstr is s simple wrapper for al_ustr_find_str
814     * so we test using that.
815     */
816 
817    CHECK(al_ustr_find_cstr(us, 0, "") == 0);
818    CHECK(al_ustr_find_cstr(us, 10, "") == 10);
819    CHECK(al_ustr_find_cstr(us, 0, "ábd") == 1);
820    CHECK(al_ustr_find_cstr(us, 10, "ábd") == 17);
821    CHECK(al_ustr_find_cstr(us, 0, "ábz") == -1);
822 
823    al_ustr_free(us);
824 }
825 
826 /* Test al_ustr_rfind_str, al_ustr_rfind_cstr. */
t38(void)827 static void t38(void)
828 {
829    ALLEGRO_USTR *us = al_ustr_new("aábdðeéfghiíaábdðeéfghií");
830    int end = al_ustr_size(us);
831 
832    /* al_ustr_find_cstr is s simple wrapper for al_ustr_find_str
833     * so we test using that.
834     */
835 
836    CHECK(al_ustr_rfind_cstr(us, 0, "") == 0);
837    CHECK(al_ustr_rfind_cstr(us, 1, "") == 1);
838    CHECK(al_ustr_rfind_cstr(us, end, "hií") == end - 4);
839    CHECK(al_ustr_rfind_cstr(us, end - 1, "hií") == 12);
840    CHECK(al_ustr_rfind_cstr(us, end, "ábz") == -1);
841 
842    al_ustr_free(us);
843 }
844 
845 /* Test al_ustr_new_from_buffer, al_cstr_dup. */
t39(void)846 static void t39(void)
847 {
848    const char s1[] = "Корабът ми на въздушна възглавница\0е пълен със змиорки";
849    ALLEGRO_USTR *us;
850    char *s2;
851 
852    us = al_ustr_new_from_buffer(s1, sizeof(s1) - 1); /* missing NUL term. */
853    s2 = al_cstr_dup(us);
854    al_ustr_free(us);
855 
856    CHECK(0 == strcmp(s1, s2));
857    CHECK(0 == memcmp(s1, s2, sizeof(s1))); /* including NUL terminator */
858 
859    al_free(s2);
860 }
861 
862 /* Test al_ustr_assign, al_ustr_assign_cstr. */
t40(void)863 static void t40(void)
864 {
865    ALLEGRO_USTR *us1 = al_ustr_new("我隻氣墊船裝滿晒鱔");
866    ALLEGRO_USTR *us2 = al_ustr_new("Τὸ χόβερκράφτ μου εἶναι γεμᾶτο χέλια");
867 
868    CHECK(al_ustr_assign(us1, us2));
869    CHECK(0 == strcmp(al_cstr(us1), "Τὸ χόβερκράφτ μου εἶναι γεμᾶτο χέλια"));
870 
871    CHECK(al_ustr_assign_cstr(us1, "私のホバークラフトは鰻でいっぱいです"));
872    CHECK(54 == al_ustr_size(us1));
873 
874    al_ustr_free(us1);
875    al_ustr_free(us2);
876 }
877 
878 /* Test al_ustr_assign_cstr. */
t41(void)879 static void t41(void)
880 {
881    ALLEGRO_USTR *us1 = al_ustr_new("Моја лебдилица је пуна јегуља");
882    ALLEGRO_USTR *us2 = al_ustr_new("");
883 
884    CHECK(al_ustr_assign_substr(us2, us1, 9, 27));
885    CHECK(0 == strcmp(al_cstr(us2), "лебдилица"));
886 
887    /* Start > End */
888    CHECK(al_ustr_assign_substr(us2, us1, 9, 0));
889    CHECK(0 == strcmp(al_cstr(us2), ""));
890 
891    /* Start, end out of bounds */
892    CHECK(al_ustr_assign_substr(us2, us1, -INT_MAX, INT_MAX));
893    CHECK(0 == strcmp(al_cstr(us2), "Моја лебдилица је пуна јегуља"));
894 
895    al_ustr_free(us1);
896    al_ustr_free(us2);
897 }
898 
899 /* Test al_ustr_set_chr. */
t42(void)900 static void t42(void)
901 {
902    ALLEGRO_USTR *us = al_ustr_new("abcdef");
903 
904    /* Same size (ASCII). */
905    CHECK(al_ustr_set_chr(us, 1, 'B') == 1);
906    CHECK(0 == strcmp(al_cstr(us), "aBcdef"));
907    CHECK(6 == al_ustr_size(us));
908 
909    /* Enlarge to 2-bytes. */
910    CHECK(al_ustr_set_chr(us, 1, U_beta) == 2);
911    CHECK(0 == strcmp(al_cstr(us), "aβcdef"));
912    CHECK(7 == al_ustr_size(us));
913 
914    /* Enlarge to 3-bytes. */
915    CHECK(al_ustr_set_chr(us, 5, U_1d08) == 3);
916    CHECK(0 == strcmp(al_cstr(us), "aβcdᴈf"));
917    CHECK(9 == al_ustr_size(us));
918 
919    /* Reduce to 2-bytes. */
920    CHECK(al_ustr_set_chr(us, 5, U_schwa) == 2);
921    CHECK(0 == strcmp(al_cstr(us), "aβcdəf"));
922    CHECK(8 == al_ustr_size(us));
923 
924    /* Set at end of string. */
925    CHECK(al_ustr_set_chr(us, al_ustr_size(us), U_1ff7) == 3);
926    CHECK(0 == strcmp(al_cstr(us), "aβcdəfῷ"));
927    CHECK(11 == al_ustr_size(us));
928 
929    /* Set past end of string. */
930    CHECK(al_ustr_set_chr(us, al_ustr_size(us) + 2, U_2051) == 3);
931    CHECK(0 == memcmp(al_cstr(us), "aβcdəfῷ\0\0⁑", 16));
932    CHECK(16 == al_ustr_size(us));
933 
934    /* Set before start of string (not allowed). */
935    CHECK(al_ustr_set_chr(us, -1, U_2051) == 0);
936    CHECK(16 == al_ustr_size(us));
937 
938    al_ustr_free(us);
939 }
940 
941 /* Test al_ustr_remove_chr. */
t43(void)942 static void t43(void)
943 {
944    ALLEGRO_USTR *us = al_ustr_new("«aβῷ»");
945 
946    CHECK(al_ustr_remove_chr(us, 2));
947    CHECK(0 == strcmp(al_cstr(us), "«βῷ»"));
948 
949    CHECK(al_ustr_remove_chr(us, 2));
950    CHECK(0 == strcmp(al_cstr(us), "«ῷ»"));
951 
952    CHECK(al_ustr_remove_chr(us, 2));
953    CHECK(0 == strcmp(al_cstr(us), "«»"));
954 
955    /* Not at beginning of code point. */
956    CHECK(! al_ustr_remove_chr(us, 1));
957 
958    /* Out of bounds. */
959    CHECK(! al_ustr_remove_chr(us, -1));
960    CHECK(! al_ustr_remove_chr(us, al_ustr_size(us)));
961 
962    al_ustr_free(us);
963 }
964 
965 /* Test al_ustr_replace_range. */
t44(void)966 static void t44(void)
967 {
968    ALLEGRO_USTR *us1 = al_ustr_new("Šis kungs par visu samaksās");
969    ALLEGRO_USTR *us2 = al_ustr_new("ī kundze");
970 
971    CHECK(al_ustr_replace_range(us1, 2, 10, us2));
972    CHECK(0 == strcmp(al_cstr(us1), "Šī kundze par visu samaksās"));
973 
974    /* Insert into itself. */
975    CHECK(al_ustr_replace_range(us1, 5, 11, us1));
976    CHECK(0 == strcmp(al_cstr(us1),
977          "Šī Šī kundze par visu samaksās par visu samaksās"));
978 
979    al_ustr_free(us1);
980    al_ustr_free(us2);
981 }
982 
983 /* Test al_ustr_replace_range (part 2). */
t45(void)984 static void t45(void)
985 {
986    ALLEGRO_USTR *us1 = al_ustr_new("abcdef");
987    ALLEGRO_USTR *us2 = al_ustr_new("ABCDEF");
988 
989    /* Start1 < 0 [not allowed] */
990    CHECK(! al_ustr_replace_range(us1, -1, 1, us2));
991 
992    /* Start1 > end(us1) [padded] */
993    CHECK(al_ustr_replace_range(us1, 8, 100, us2));
994    CHECK(0 == memcmp(al_cstr(us1), "abcdef\0\0ABCDEF", 15));
995 
996    /* Start1 > end1 [not allowed] */
997    CHECK(! al_ustr_replace_range(us1, 8, 1, us2));
998    CHECK(0 == memcmp(al_cstr(us1), "abcdef\0\0ABCDEF", 15));
999 
1000    al_ustr_free(us1);
1001    al_ustr_free(us2);
1002 }
1003 
1004 extern bool call_vappendf(ALLEGRO_USTR *us, const char *fmt, ...);
1005 
1006 /* Test al_ustr_newf, al_ustr_appendf, al_ustr_vappendf. */
t46(void)1007 static void t46(void)
1008 {
1009    ALLEGRO_USTR *us;
1010 
1011    us = al_ustr_newf("%s %c %.2f %.02d", "hõljuk", 'c', ALLEGRO_PI, 42);
1012    CHECK(0 == strcmp(al_cstr(us), "hõljuk c 3.14 42"));
1013 
1014    CHECK(al_ustr_appendf(us, " %s", "Luftchüssiboot"));
1015    CHECK(0 == strcmp(al_cstr(us), "hõljuk c 3.14 42 Luftchüssiboot"));
1016 
1017    CHECK(call_vappendf(us, " %s", "χόβερκράφτ"));
1018    CHECK(0 == strcmp(al_cstr(us), "hõljuk c 3.14 42 Luftchüssiboot χόβερκράφτ"));
1019 
1020    al_ustr_free(us);
1021 
1022    us = al_ustr_new("");
1023    call_vappendf(us, "%s", "test");
1024    CHECK(0 == strcmp(al_cstr(us), "test"));
1025    al_ustr_free(us);
1026 }
1027 
call_vappendf(ALLEGRO_USTR * us,const char * fmt,...)1028 bool call_vappendf(ALLEGRO_USTR *us, const char *fmt, ...)
1029 {
1030    va_list ap;
1031    bool rc;
1032 
1033    va_start(ap, fmt);
1034    rc = al_ustr_vappendf(us, fmt, ap);
1035    va_end(ap);
1036    return rc;
1037 }
1038 
1039 /* Test al_ustr_compare, al_ustr_ncompare. */
t47(void)1040 static void t47(void)
1041 {
1042    ALLEGRO_USTR_INFO i1;
1043    ALLEGRO_USTR_INFO i2;
1044 
1045    CHECK(al_ustr_compare(
1046             al_ref_cstr(&i1, "Thú mỏ vịt"),
1047             al_ref_cstr(&i2, "Thú mỏ vịt")) == 0);
1048 
1049    CHECK(al_ustr_compare(
1050             al_ref_cstr(&i1, "Thú mỏ vị"),
1051             al_ref_cstr(&i2, "Thú mỏ vịt")) < 0);
1052 
1053    CHECK(al_ustr_compare(
1054             al_ref_cstr(&i1, "Thú mỏ vịt"),
1055             al_ref_cstr(&i2, "Thú mỏ vit")) > 0);
1056 
1057    CHECK(al_ustr_compare(
1058             al_ref_cstr(&i1, "abc"),
1059             al_ref_cstr(&i2, "abc\001")) < 0);
1060 
1061    CHECK(al_ustr_compare(
1062             al_ref_cstr(&i1, "abc\001"),
1063             al_ref_cstr(&i2, "abc")) > 0);
1064 
1065    CHECK(al_ustr_ncompare(
1066             al_ref_cstr(&i1, "Thú mỏ vịt"),
1067             al_ref_cstr(&i2, "Thú mỏ vit"), 8) == 0);
1068 
1069    CHECK(al_ustr_ncompare(
1070             al_ref_cstr(&i1, "Thú mỏ vịt"),
1071             al_ref_cstr(&i2, "Thú mỏ vit"), 9) > 0);
1072 
1073    CHECK(al_ustr_ncompare(
1074             al_ref_cstr(&i1, "Thú mỏ vịt"),
1075             al_ref_cstr(&i2, "platypus"), 0) == 0);
1076 
1077    CHECK(al_ustr_ncompare(
1078             al_ref_cstr(&i1, "abc"),
1079             al_ref_cstr(&i2, "abc\001"), 4) < 0);
1080 
1081    CHECK(al_ustr_ncompare(
1082             al_ref_cstr(&i1, "abc\001"),
1083             al_ref_cstr(&i2, "abc"), 4) > 0);
1084 
1085 }
1086 
1087 /* Test al_ustr_has_prefix, al_ustr_has_suffix. */
t48(void)1088 static void t48(void)
1089 {
1090    ALLEGRO_USTR_INFO i1;
1091    const ALLEGRO_USTR *us1 = al_ref_cstr(&i1, "Thú mỏ vịt");
1092 
1093    /* The _cstr versions are simple wrappers around the real functions so its
1094     * okay to test them only.
1095     */
1096 
1097    CHECK(al_ustr_has_prefix_cstr(us1, ""));
1098    CHECK(al_ustr_has_prefix_cstr(us1, "Thú"));
1099    CHECK(! al_ustr_has_prefix_cstr(us1, "Thú mỏ vịt."));
1100 
1101    CHECK(al_ustr_has_suffix_cstr(us1, ""));
1102    CHECK(al_ustr_has_suffix_cstr(us1, "vịt"));
1103    CHECK(! al_ustr_has_suffix_cstr(us1, "Thú mỏ vịt."));
1104 }
1105 
1106 /* Test al_ustr_find_replace, al_ustr_find_replace_cstr. */
t49(void)1107 static void t49(void)
1108 {
1109    ALLEGRO_USTR *us;
1110    ALLEGRO_USTR_INFO findi;
1111    ALLEGRO_USTR_INFO repli;
1112    const ALLEGRO_USTR *find;
1113    const ALLEGRO_USTR *repl;
1114 
1115    us = al_ustr_new("aábdðeéfghiíaábdðeéfghií");
1116    find = al_ref_cstr(&findi, "ðeéf");
1117    repl = al_ref_cstr(&repli, "deef");
1118 
1119    CHECK(al_ustr_find_replace(us, 0, find, repl));
1120    CHECK(0 == strcmp(al_cstr(us), "aábddeefghiíaábddeefghií"));
1121 
1122    find = al_ref_cstr(&findi, "aá");
1123    repl = al_ref_cstr(&repli, "AÁ");
1124 
1125    CHECK(al_ustr_find_replace(us, 14, find, repl));
1126    CHECK(0 == strcmp(al_cstr(us), "aábddeefghiíAÁbddeefghií"));
1127 
1128    CHECK(al_ustr_find_replace_cstr(us, 0, "dd", "đ"));
1129    CHECK(0 == strcmp(al_cstr(us), "aábđeefghiíAÁbđeefghií"));
1130 
1131    /* Not allowed */
1132    find = al_ustr_empty_string();
1133    CHECK(! al_ustr_find_replace(us, 0, find, repl));
1134    CHECK(0 == strcmp(al_cstr(us), "aábđeefghiíAÁbđeefghií"));
1135 
1136    al_ustr_free(us);
1137 }
1138 
1139 /* Test UTF-16 conversion. */
t50(void)1140 static void t50(void)
1141 {
1142    ALLEGRO_USTR *us;
1143    char utf8[] = "⅛-note: ��, domino: ��";
1144    uint16_t *utf16;
1145    size_t s;
1146    uint16_t little[8];
1147    /* Only native byte order supported right now, so have to specify
1148     * elements as uint16_t and not as char.
1149     */
1150    uint16_t utf16_ref[] = {
1151       0x215b, 0x002d, 0x006e, 0x006f, 0x0074,
1152       0x0065, 0x003a, 0x0020, 0xd834, 0xdd60,
1153       0x002c, 0x0020, 0x0064, 0x006f, 0x006d,
1154       0x0069, 0x006e, 0x006f, 0x003a, 0x0020,
1155       0xd83c, 0xdc61, 0x0000};
1156    uint16_t truncated[] = {
1157       0x215b, 0x002d, 0x006e, 0x006f, 0x0074,
1158       0x0065, 0x003a, 0x0000};
1159 
1160    us = al_ustr_new_from_utf16(utf16_ref);
1161    CHECK(20 == al_ustr_length(us));
1162    CHECK(0 == strcmp(utf8, al_cstr(us)));
1163    al_ustr_free(us);
1164 
1165    us = al_ustr_new(utf8);
1166    s = al_ustr_size_utf16(us);
1167    CHECK(46 == s);
1168    utf16 = malloc(s);
1169    al_ustr_encode_utf16(us, utf16, s);
1170    CHECK(0 == memcmp(utf16, utf16_ref, s));
1171    free(utf16);
1172 
1173    s = al_ustr_encode_utf16(us, little, sizeof little);
1174    CHECK(16 == s);
1175    CHECK(0 == memcmp(truncated, little, s));
1176    al_ustr_free(us);
1177 }
1178 
1179 /* Test al_ustr_to_buffer */
t51(void)1180 static void t51(void)
1181 {
1182    char str[256];
1183    const ALLEGRO_USTR *us;
1184    ALLEGRO_USTR_INFO info;
1185 
1186    us = al_ref_buffer(&info, "Allegro", 3);
1187    al_ustr_to_buffer(us, str, 10);
1188    CHECK(0 == memcmp(str, "All", 4));
1189    al_ustr_to_buffer(us, str, 4);
1190    CHECK(0 == memcmp(str, "All", 4));
1191    al_ustr_to_buffer(us, str, 3);
1192    CHECK(0 == memcmp(str, "Al", 3));
1193 }
1194 
1195 /*---------------------------------------------------------------------------*/
1196 
1197 const test_t all_tests[] =
1198 {
1199    NULL, t1, t2, t3, t4, t5, t6, t7, t8, t9,
1200    t10, t11, t12, t13, t14, t15, t16, t17, t18, t19,
1201    t20, t21, t22, t23, t24, t25, t26, t27, t28, t29,
1202    t30, t31, t32, t33, t34, t35, t36, t37, t38, t39,
1203    t40, t41, t42, t43, t44, t45, t46, t47, t48, t49,
1204    t50, t51
1205 };
1206 
1207 #define NUM_TESTS (int)(sizeof(all_tests) / sizeof(all_tests[0]))
1208 
main(int argc,char ** argv)1209 int main(int argc, char **argv)
1210 {
1211    int i;
1212 
1213    if (!al_init()) {
1214       abort_example("Could not init Allegro.\n");
1215    }
1216    open_log_monospace();
1217 
1218    if (argc < 2) {
1219       for (i = 1; i < NUM_TESTS; i++) {
1220          log_printf("# t%d\n\n", i);
1221          all_tests[i]();
1222          log_printf("\n");
1223       }
1224    }
1225    else {
1226       i = atoi(argv[1]);
1227       if (i > 0 && i < NUM_TESTS) {
1228          all_tests[i]();
1229       }
1230    }
1231    log_printf("Done\n");
1232 
1233    close_log(true);
1234 
1235    if (error) {
1236       exit(EXIT_FAILURE);
1237    }
1238 
1239    return 0;
1240 }
1241 
1242 /* vim: set sts=3 sw=3 et: */
1243