1 /* See LICENSE file for copyright and license details. */
7 #include "../grapheme.h"
10 struct unit_test_is_case_utf8 {
11 const char *description;
22 struct unit_test_to_case_utf8 {
23 const char *description;
35 static const struct unit_test_is_case_utf8 is_lowercase_utf8[] = {
37 .description = "empty input",
39 .output = { true, 0 },
42 .description = "one character, violation",
44 .output = { false, 0 },
47 .description = "one character, confirmation",
48 .input = { "\xC3\x9F", 2 },
49 .output = { true, 2 },
52 .description = "one character, violation, NUL-terminated",
53 .input = { "A", SIZE_MAX },
54 .output = { false, 0 },
57 .description = "one character, confirmation, NUL-terminated",
58 .input = { "\xC3\x9F", SIZE_MAX },
59 .output = { true, 2 },
62 .description = "one word, violation",
63 .input = { "Hello", 5 },
64 .output = { false, 0 },
67 .description = "one word, partial confirmation",
68 .input = { "gru" "\xC3\x9F" "fOrmel", 11 },
69 .output = { false, 6 },
72 .description = "one word, full confirmation",
73 .input = { "gru" "\xC3\x9F" "formel", 11 },
74 .output = { true, 11 },
77 .description = "one word, violation, NUL-terminated",
78 .input = { "Hello", SIZE_MAX },
79 .output = { false, 0 },
82 .description = "one word, partial confirmation, NUL-terminated",
83 .input = { "gru" "\xC3\x9F" "fOrmel", SIZE_MAX },
84 .output = { false, 6 },
87 .description = "one word, full confirmation, NUL-terminated",
88 .input = { "gru" "\xC3\x9F" "formel", SIZE_MAX },
89 .output = { true, 11 },
93 static const struct unit_test_is_case_utf8 is_uppercase_utf8[] = {
95 .description = "empty input",
97 .output = { true, 0 },
100 .description = "one character, violation",
101 .input = { "\xC3\x9F", 2 },
102 .output = { false, 0 },
105 .description = "one character, confirmation",
107 .output = { true, 1 },
110 .description = "one character, violation, NUL-terminated",
111 .input = { "\xC3\x9F", SIZE_MAX },
112 .output = { false, 0 },
115 .description = "one character, confirmation, NUL-terminated",
116 .input = { "A", SIZE_MAX },
117 .output = { true, 1 },
120 .description = "one word, violation",
121 .input = { "hello", 5 },
122 .output = { false, 0 },
125 .description = "one word, partial confirmation",
126 .input = { "GRU" "\xC3\x9F" "formel", 11 },
127 .output = { false, 3 },
130 .description = "one word, full confirmation",
131 .input = { "HELLO", 5 },
132 .output = { true, 5 },
135 .description = "one word, violation, NUL-terminated",
136 .input = { "hello", SIZE_MAX },
137 .output = { false, 0 },
140 .description = "one word, partial confirmation, NUL-terminated",
141 .input = { "GRU" "\xC3\x9F" "formel", SIZE_MAX },
142 .output = { false, 3 },
145 .description = "one word, full confirmation, NUL-terminated",
146 .input = { "HELLO", SIZE_MAX },
147 .output = { true, 5 },
151 static const struct unit_test_is_case_utf8 is_titlecase_utf8[] = {
153 .description = "empty input",
155 .output = { true, 0 },
158 .description = "one character, violation",
159 .input = { "\xC3\x9F", 2 },
160 .output = { false, 0 },
163 .description = "one character, confirmation",
165 .output = { true, 1 },
168 .description = "one character, violation, NUL-terminated",
169 .input = { "\xC3\x9F", SIZE_MAX },
170 .output = { false, 0 },
173 .description = "one character, confirmation, NUL-terminated",
174 .input = { "A", SIZE_MAX },
175 .output = { true, 1 },
178 .description = "one word, violation",
179 .input = { "hello", 5 },
180 .output = { false, 0 },
183 .description = "one word, partial confirmation",
184 .input = { "Gru" "\xC3\x9F" "fOrmel", 11 },
185 .output = { false, 6 },
188 .description = "one word, full confirmation",
189 .input = { "Gru" "\xC3\x9F" "formel", 11 },
190 .output = { true, 11 },
193 .description = "one word, violation, NUL-terminated",
194 .input = { "hello", SIZE_MAX },
195 .output = { false, 0 },
198 .description = "one word, partial confirmation, NUL-terminated",
199 .input = { "Gru" "\xC3\x9F" "fOrmel", SIZE_MAX },
200 .output = { false, 6 },
203 .description = "one word, full confirmation, NUL-terminated",
204 .input = { "Gru" "\xC3\x9F" "formel", SIZE_MAX },
205 .output = { true, 11 },
208 .description = "multiple words, partial confirmation",
209 .input = { "Hello Gru" "\xC3\x9F" "fOrmel!", 18 },
210 .output = { false, 12 },
213 .description = "multiple words, full confirmation",
214 .input = { "Hello Gru" "\xC3\x9F" "formel!", 18 },
215 .output = { true, 18 },
218 .description = "multiple words, partial confirmation, NUL-terminated",
219 .input = { "Hello Gru" "\xC3\x9F" "fOrmel!", SIZE_MAX },
220 .output = { false, 12 },
223 .description = "multiple words, full confirmation, NUL-terminated",
224 .input = { "Hello Gru" "\xC3\x9F" "formel!", SIZE_MAX },
225 .output = { true, 18 },
229 static const struct unit_test_to_case_utf8 to_lowercase_utf8[] = {
231 .description = "empty input",
232 .input = { "", 0, 10 },
236 .description = "empty output",
237 .input = { "hello", 5, 0 },
241 .description = "one character, conversion",
242 .input = { "A", 1, 10 },
243 .output = { "a", 1 },
246 .description = "one character, no conversion",
247 .input = { "\xC3\x9F", 2, 10 },
248 .output = { "\xC3\x9F", 2 },
251 .description = "one character, conversion, truncation",
252 .input = { "A", 1, 0 },
256 .description = "one character, conversion, NUL-terminated",
257 .input = { "A", SIZE_MAX, 10 },
258 .output = { "a", 1 },
261 .description = "one character, no conversion, NUL-terminated",
262 .input = { "\xC3\x9F", SIZE_MAX, 10 },
263 .output = { "\xC3\x9F", 2 },
266 .description = "one character, conversion, NUL-terminated, truncation",
267 .input = { "A", SIZE_MAX, 0 },
271 .description = "one word, conversion",
272 .input = { "wOrD", 4, 10 },
273 .output = { "word", 4 },
276 .description = "one word, no conversion",
277 .input = { "word", 4, 10 },
278 .output = { "word", 4 },
281 .description = "one word, conversion, truncation",
282 .input = { "wOrD", 4, 3 },
283 .output = { "wo", 4 },
286 .description = "one word, conversion, NUL-terminated",
287 .input = { "wOrD", SIZE_MAX, 10 },
288 .output = { "word", 4 },
291 .description = "one word, no conversion, NUL-terminated",
292 .input = { "word", SIZE_MAX, 10 },
293 .output = { "word", 4 },
296 .description = "one word, conversion, NUL-terminated, truncation",
297 .input = { "wOrD", SIZE_MAX, 3 },
298 .output = { "wo", 4 },
302 static const struct unit_test_to_case_utf8 to_uppercase_utf8[] = {
304 .description = "empty input",
305 .input = { "", 0, 10 },
309 .description = "empty output",
310 .input = { "hello", 5, 0 },
314 .description = "one character, conversion",
315 .input = { "\xC3\x9F", 2, 10 },
316 .output = { "SS", 2 },
319 .description = "one character, no conversion",
320 .input = { "A", 1, 10 },
321 .output = { "A", 1 },
324 .description = "one character, conversion, truncation",
325 .input = { "\xC3\x9F", 2, 0 },
329 .description = "one character, conversion, NUL-terminated",
330 .input = { "\xC3\x9F", SIZE_MAX, 10 },
331 .output = { "SS", 2 },
334 .description = "one character, no conversion, NUL-terminated",
335 .input = { "A", SIZE_MAX, 10 },
336 .output = { "A", 1 },
339 .description = "one character, conversion, NUL-terminated, truncation",
340 .input = { "\xC3\x9F", SIZE_MAX, 0 },
344 .description = "one word, conversion",
345 .input = { "gRu" "\xC3\x9F" "fOrMel", 11, 15 },
346 .output = { "GRUSSFORMEL", 11 },
349 .description = "one word, no conversion",
350 .input = { "WORD", 4, 10 },
351 .output = { "WORD", 4 },
354 .description = "one word, conversion, truncation",
355 .input = { "gRu" "\xC3\x9F" "formel", 11, 5 },
356 .output = { "GRUS", 11 },
359 .description = "one word, conversion, NUL-terminated",
360 .input = { "gRu" "\xC3\x9F" "formel", SIZE_MAX, 15 },
361 .output = { "GRUSSFORMEL", 11 },
364 .description = "one word, no conversion, NUL-terminated",
365 .input = { "WORD", SIZE_MAX, 10 },
366 .output = { "WORD", 4 },
369 .description = "one word, conversion, NUL-terminated, truncation",
370 .input = { "gRu" "\xC3\x9F" "formel", SIZE_MAX, 5 },
371 .output = { "GRUS", 11 },
375 static const struct unit_test_to_case_utf8 to_titlecase_utf8[] = {
377 .description = "empty input",
378 .input = { "", 0, 10 },
382 .description = "empty output",
383 .input = { "hello", 5, 0 },
387 .description = "one character, conversion",
388 .input = { "a", 1, 10 },
389 .output = { "A", 1 },
392 .description = "one character, no conversion",
393 .input = { "A", 1, 10 },
394 .output = { "A", 1 },
397 .description = "one character, conversion, truncation",
398 .input = { "a", 1, 0 },
402 .description = "one character, conversion, NUL-terminated",
403 .input = { "a", SIZE_MAX, 10 },
404 .output = { "A", 1 },
407 .description = "one character, no conversion, NUL-terminated",
408 .input = { "A", SIZE_MAX, 10 },
409 .output = { "A", 1 },
412 .description = "one character, conversion, NUL-terminated, truncation",
413 .input = { "a", SIZE_MAX, 0 },
417 .description = "one word, conversion",
418 .input = { "heLlo", 5, 10 },
419 .output = { "Hello", 5 },
422 .description = "one word, no conversion",
423 .input = { "Hello", 5, 10 },
424 .output = { "Hello", 5 },
427 .description = "one word, conversion, truncation",
428 .input = { "heLlo", 5, 2 },
429 .output = { "H", 5 },
432 .description = "one word, conversion, NUL-terminated",
433 .input = { "heLlo", SIZE_MAX, 10 },
434 .output = { "Hello", 5 },
437 .description = "one word, no conversion, NUL-terminated",
438 .input = { "Hello", SIZE_MAX, 10 },
439 .output = { "Hello", 5 },
442 .description = "one word, conversion, NUL-terminated, truncation",
443 .input = { "heLlo", SIZE_MAX, 3 },
444 .output = { "He", 5 },
447 .description = "two words, conversion",
448 .input = { "heLlo wORLd!", 12, 20 },
449 .output = { "Hello World!", 12 },
452 .description = "two words, no conversion",
453 .input = { "Hello World!", 12, 20 },
454 .output = { "Hello World!", 12 },
457 .description = "two words, conversion, truncation",
458 .input = { "heLlo wORLd!", 12, 8 },
459 .output = { "Hello W", 12 },
462 .description = "two words, conversion, NUL-terminated",
463 .input = { "heLlo wORLd!", SIZE_MAX, 20 },
464 .output = { "Hello World!", 12 },
467 .description = "two words, no conversion, NUL-terminated",
468 .input = { "Hello World!", SIZE_MAX, 20 },
469 .output = { "Hello World!", 12 },
472 .description = "two words, conversion, NUL-terminated, truncation",
473 .input = { "heLlo wORLd!", SIZE_MAX, 4 },
474 .output = { "Hel", 12 },
479 unit_test_callback_is_case_utf8(const void *t, size_t off, const char *name,
482 const struct unit_test_is_case_utf8 *test =
483 (const struct unit_test_is_case_utf8 *)t + off;
485 size_t caselen = 0x7f;
487 if (t == is_lowercase_utf8) {
488 ret = grapheme_is_lowercase_utf8(test->input.src, test->input.srclen,
490 } else if (t == is_uppercase_utf8) {
491 ret = grapheme_is_uppercase_utf8(test->input.src, test->input.srclen,
493 } else if (t == is_titlecase_utf8) {
494 ret = grapheme_is_titlecase_utf8(test->input.src, test->input.srclen,
502 if (ret != test->output.ret || caselen != test->output.caselen) {
508 fprintf(stderr, "%s: %s: Failed unit test %zu \"%s\" "
509 "(returned (%s, %zu) instead of (%s, %zu)).\n", argv0,
510 name, off, test->description, ret ? "true" : "false",
511 caselen, test->output.ret ? "true" : "false",
512 test->output.caselen);
517 unit_test_callback_to_case_utf8(const void *t, size_t off, const char *name,
520 const struct unit_test_to_case_utf8 *test =
521 (const struct unit_test_to_case_utf8 *)t + off;
525 /* fill the array with canary values */
526 memset(buf, 0x7f, LEN(buf));
528 if (t == to_lowercase_utf8) {
529 ret = grapheme_to_lowercase_utf8(test->input.src, test->input.srclen,
530 buf, test->input.destlen);
531 } else if (t == to_uppercase_utf8) {
532 ret = grapheme_to_uppercase_utf8(test->input.src, test->input.srclen,
533 buf, test->input.destlen);
534 } else if (t == to_titlecase_utf8) {
535 ret = grapheme_to_titlecase_utf8(test->input.src, test->input.srclen,
536 buf, test->input.destlen);
542 if (ret != test->output.ret ||
543 memcmp(buf, test->output.dest, MIN(test->input.destlen, test->output.ret))) {
547 /* check that none of the canary values have been overwritten */
548 for (i = test->input.destlen; i < LEN(buf); i++) {
549 if (buf[i] != 0x7f) {
556 fprintf(stderr, "%s: %s: Failed unit test %zu \"%s\" "
557 "(returned (\"%.*s\", %zu) instead of (\"%.*s\", %zu)).\n", argv0,
558 name, off, test->description, (int)ret, buf, ret,
559 (int)test->output.ret, test->output.dest, test->output.ret);
564 main(int argc, char *argv[])
568 return run_unit_tests(unit_test_callback_is_case_utf8, is_lowercase_utf8,
569 LEN(is_lowercase_utf8), "grapheme_is_lowercase_utf8", argv[0]) +
570 run_unit_tests(unit_test_callback_is_case_utf8, is_uppercase_utf8,
571 LEN(is_uppercase_utf8), "grapheme_is_uppercase_utf8", argv[0]) +
572 run_unit_tests(unit_test_callback_is_case_utf8, is_titlecase_utf8,
573 LEN(is_titlecase_utf8), "grapheme_is_titlecase_utf8", argv[0]) +
574 run_unit_tests(unit_test_callback_to_case_utf8, to_lowercase_utf8,
575 LEN(to_lowercase_utf8), "grapheme_to_lowercase_utf8", argv[0]) +
576 run_unit_tests(unit_test_callback_to_case_utf8, to_uppercase_utf8,
577 LEN(to_uppercase_utf8), "grapheme_to_uppercase_utf8", argv[0]) +
578 run_unit_tests(unit_test_callback_to_case_utf8, to_titlecase_utf8,
579 LEN(to_titlecase_utf8), "grapheme_to_titlecase_utf8", argv[0]);