xref: /illumos-gate/usr/src/test/libc-tests/tests/uchar.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2020 Robert Mustacchi
14  */
15 
16 /*
17  * Test the implementation of various pieces of uchar.h(3HEAD) functionality.
18  */
19 
20 #include <locale.h>
21 #include <err.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/sysmacros.h>
25 #include <strings.h>
26 #include <wchar.h>
27 #include <uchar.h>
28 #include <errno.h>
29 
30 static const char *uchar_wide = "光";
31 static const char32_t uchar_value = 0x5149;
32 static const char *uchar_hello = "hello";
33 
34 static void
35 update_locale(const char *loc)
36 {
37 	const char *newloc = setlocale(LC_CTYPE, loc);
38 	if (newloc == NULL) {
39 		err(EXIT_FAILURE, "TEST FAILED: failed to update locale to %s",
40 		    loc);
41 	}
42 
43 	if (strcmp(newloc, loc) != 0) {
44 		errx(EXIT_FAILURE, "TEST FAILED: locale set to %s, but got %s",
45 		    loc, newloc);
46 	}
47 }
48 
49 static boolean_t
50 mbrtoc32_ascii(mbstate_t *mbs)
51 {
52 	char32_t out;
53 	size_t len;
54 	boolean_t ret = B_TRUE;
55 
56 	if ((len = mbrtoc32(&out, uchar_hello, 5, mbs)) != 1) {
57 		warnx("expected mbrtoc32 to return 1, returned %zu", len);
58 		ret = B_FALSE;
59 	}
60 
61 	if (out != 'h') {
62 		warnx("got bad char32_t, expected 0x%x, found 0x%x\n", 'h',
63 		    out);
64 		ret = B_FALSE;
65 	}
66 
67 	if ((len = mbrtoc32(&out, uchar_hello + 1, 4, mbs)) != 1) {
68 		warnx("expected mbrtoc32 to return 1, returned %zu", len);
69 		ret = B_FALSE;
70 	}
71 
72 	if (out != 'e') {
73 		warnx("got bad char32_t, expected 0x%x, found 0x%x\n", 'h',
74 		    out);
75 		ret = B_FALSE;
76 	}
77 
78 	return (ret);
79 }
80 
81 static boolean_t
82 mbrtoc32_ascii_internal(void)
83 {
84 	return (mbrtoc32_ascii(NULL));
85 }
86 
87 static boolean_t
88 mbrtoc32_ascii_mbstate(void)
89 {
90 	mbstate_t mbs;
91 
92 	bzero(&mbs, sizeof (mbs));
93 	return (mbrtoc32_ascii(&mbs));
94 }
95 
96 static boolean_t
97 mbrtoc32_badseq_utf8(void)
98 {
99 	mbstate_t mbs;
100 	size_t len;
101 	char32_t out;
102 	boolean_t ret = B_TRUE;
103 	char *badstr;
104 
105 	bzero(&mbs, sizeof (mbs));
106 	len = mbrtoc32(&out, "\xa9", 1, &mbs);
107 	if (len != (size_t)-1) {
108 		warnx("mbrtoc32 returned %zu, not %zu", len, (size_t)-1);
109 		ret = B_FALSE;
110 	}
111 
112 	if (errno != EILSEQ) {
113 		warnx("found bad errno, expected %d, found %d\n", errno,
114 		    EILSEQ);
115 		ret = B_FALSE;
116 	}
117 
118 	badstr = strdup(uchar_wide);
119 	if (badstr == NULL) {
120 		warn("failed to duplicate uchar_wide");
121 		return (B_FALSE);
122 	}
123 
124 	badstr[1] = '?';
125 	bzero(&mbs, sizeof (mbs));
126 	len = mbrtoc32(&out, badstr, strlen(badstr), &mbs);
127 	free(badstr);
128 	if (len != (size_t)-1) {
129 		warnx("mbrtoc32 returned %zu, not %zu", len, (size_t)-1);
130 		ret = B_FALSE;
131 	}
132 
133 	if (errno != EILSEQ) {
134 		warnx("found bad errno, expected %d, found %d\n", errno,
135 		    EILSEQ);
136 		ret = B_FALSE;
137 	}
138 
139 	return (ret);
140 }
141 
142 static boolean_t
143 mbrtoc32_roundtrip(void)
144 {
145 	char32_t out;
146 	size_t len, clen;
147 	mbstate_t mbs;
148 	char buf[MB_CUR_MAX];
149 	boolean_t ret = B_TRUE;
150 
151 	bzero(&mbs, sizeof (mbs));
152 	len = mbrtoc32(&out, uchar_wide, strlen(uchar_wide), &mbs);
153 	if (len != 3) {
154 		warnx("mbrtoc32 returned %zu, expected %u", len, 3);
155 		ret = B_FALSE;
156 	}
157 
158 	if (out != uchar_value) {
159 		warnx("mbrtoc32 converted character to 0x%x not 0x%x",
160 		    out, uchar_value);
161 		ret = B_FALSE;
162 	}
163 
164 	clen = c32rtomb(buf, out, &mbs);
165 	if (clen != len) {
166 		warnx("c32rtomb returned %d bytes, but we originally used %d",
167 		    clen, len);
168 		ret = B_FALSE;
169 	}
170 
171 	if (strncmp(buf, uchar_wide, len) != 0) {
172 		warnx("round trip string comparison failed");
173 		ret = B_FALSE;
174 	}
175 
176 	return (ret);
177 }
178 
179 static boolean_t
180 mbrtoc32_partial(void)
181 {
182 	char32_t out;
183 	size_t len, i;
184 	mbstate_t mbs;
185 	boolean_t ret = B_TRUE;
186 
187 	bzero(&mbs, sizeof (mbs));
188 	for (i = 0; i < strlen(uchar_wide) - 1; i++) {
189 		len = mbrtoc32(&out, uchar_wide + i, 1, &mbs);
190 		if (len != (size_t)-2) {
191 			warnx("partial mbrtoc32 returned %zu, not -2", len);
192 			ret = B_FALSE;
193 		}
194 	}
195 
196 	len = mbrtoc32(&out, uchar_wide + i, 1, &mbs);
197 	if (len != 1) {
198 		warnx("partial mbrtoc32 returned %zu, not 1", len);
199 		ret = B_FALSE;
200 	}
201 
202 	if (out != uchar_value) {
203 		warnx("mbrtoc32 converted character to 0x%x not 0x%x",
204 		    out, uchar_value);
205 		ret = B_FALSE;
206 	}
207 
208 	return (ret);
209 }
210 
211 static boolean_t
212 mbrtoc32_zero(void)
213 {
214 	char32_t out, exp = L'\0';
215 	size_t len;
216 	mbstate_t mbs;
217 	boolean_t ret = B_TRUE;
218 
219 	bzero(&mbs, sizeof (mbs));
220 	len = mbrtoc32(&out, "", 1, &mbs);
221 	if (len != 0) {
222 		warnx("partial mbrtoc32 returned %zu, not 0", len);
223 		ret = B_FALSE;
224 	}
225 
226 	if (out != exp) {
227 		warnx("mbrtoc32 converted character to 0x%x not 0x%x",
228 		    out, exp);
229 		ret = B_FALSE;
230 	}
231 
232 	return (ret);
233 }
234 
235 static boolean_t
236 mbrtoc32_zero_len(void)
237 {
238 	char32_t out = 0x12345, exp = 0x12345;
239 	size_t len;
240 	mbstate_t mbs;
241 	boolean_t ret = B_TRUE;
242 
243 	bzero(&mbs, sizeof (mbs));
244 	len = mbrtoc32(&out, uchar_wide, 0, &mbs);
245 	if (len != (size_t)-2) {
246 		warnx("partial mbrtoc32 returned %zu, not -2", len);
247 		ret = B_FALSE;
248 	}
249 
250 	if (out != exp) {
251 		warnx("mbrtoc32 incorrectly wrote to char32_t value with "
252 		    "zero string, found 0x%x not 0x%x", out, exp);
253 		ret = B_FALSE;
254 	}
255 
256 	return (ret);
257 }
258 
259 static boolean_t
260 mbrtoc32_null(void)
261 {
262 	char32_t out = 0x123456, exp = 0x123456;
263 	size_t len;
264 	mbstate_t mbs;
265 	boolean_t ret = B_TRUE;
266 
267 	bzero(&mbs, sizeof (mbs));
268 	len = mbrtoc32(&out, NULL, 1, &mbs);
269 	if (len != 0) {
270 		warnx("partial mbrtoc32 returned %zu, not 0", len);
271 		ret = B_FALSE;
272 	}
273 
274 	if (out != exp) {
275 		warnx("mbrtoc32 incorrectly wrote to char32_t value with "
276 		    "null string, found 0x%x not 0x%x", out, exp);
277 		ret = B_FALSE;
278 	}
279 
280 	return (ret);
281 }
282 
283 static boolean_t
284 mbrtoc16_ascii(mbstate_t *mbs)
285 {
286 	char16_t out;
287 	size_t len;
288 	boolean_t ret = B_TRUE;
289 
290 	if ((len = mbrtoc16(&out, uchar_hello, 5, mbs)) != 1) {
291 		warnx("expected mbrtoc16 to return 1, returned %zu", len);
292 		ret = B_FALSE;
293 	}
294 
295 	if (out != 'h') {
296 		warnx("got bad char16_t, expected 0x%x, found 0x%x\n", 'h',
297 		    out);
298 		ret = B_FALSE;
299 	}
300 
301 	if ((len = mbrtoc16(&out, uchar_hello + 1, 4, mbs)) != 1) {
302 		warnx("expected mbrtoc16 to return 1, returned %zu", len);
303 		ret = B_FALSE;
304 	}
305 
306 	if (out != 'e') {
307 		warnx("got bad char16_t, expected 0x%x, found 0x%x\n", 'h',
308 		    out);
309 		ret = B_FALSE;
310 	}
311 
312 	return (ret);
313 }
314 
315 static boolean_t
316 mbrtoc16_ascii_internal(void)
317 {
318 	return (mbrtoc16_ascii(NULL));
319 }
320 
321 static boolean_t
322 mbrtoc16_ascii_mbstate(void)
323 {
324 	mbstate_t mbs;
325 
326 	bzero(&mbs, sizeof (mbs));
327 	return (mbrtoc16_ascii(&mbs));
328 }
329 
330 static boolean_t
331 mbrtoc16_null(void)
332 {
333 	char16_t out = 0x1234, exp = 0x1234;
334 	size_t len;
335 	mbstate_t mbs;
336 	boolean_t ret = B_TRUE;
337 
338 	bzero(&mbs, sizeof (mbs));
339 	len = mbrtoc16(&out, NULL, 1, &mbs);
340 	if (len != 0) {
341 		warnx("partial mbrtoc16 returned %zu, not 0", len);
342 		ret = B_FALSE;
343 	}
344 
345 	if (out != exp) {
346 		warnx("mbrtoc16 incorrectly wrote to char16_t value with "
347 		    "null string, found 0x%x not 0x%x", out, exp);
348 		ret = B_FALSE;
349 	}
350 
351 	return (ret);
352 }
353 
354 static boolean_t
355 mbrtoc16_zero(void)
356 {
357 	char16_t out, exp = L'\0';
358 	size_t len;
359 	mbstate_t mbs;
360 	boolean_t ret = B_TRUE;
361 
362 	bzero(&mbs, sizeof (mbs));
363 	len = mbrtoc16(&out, "", 1, &mbs);
364 	if (len != 0) {
365 		warnx("partial mbrtoc16 returned %zu, not 0", len);
366 		ret = B_FALSE;
367 	}
368 
369 	if (out != exp) {
370 		warnx("mbrtoc16 converted character to 0x%x not 0x%x",
371 		    out, exp);
372 		ret = B_FALSE;
373 	}
374 
375 	return (ret);
376 }
377 
378 static boolean_t
379 mbrtoc16_zero_len(void)
380 {
381 	char16_t out = 0x5432, exp = 0x5432;
382 	size_t len;
383 	mbstate_t mbs;
384 	boolean_t ret = B_TRUE;
385 
386 	bzero(&mbs, sizeof (mbs));
387 	len = mbrtoc16(&out, uchar_wide, 0, &mbs);
388 	if (len != (size_t)-2) {
389 		warnx("partial mbrtoc16 returned %zu, not -2", len);
390 		ret = B_FALSE;
391 	}
392 
393 	if (out != exp) {
394 		warnx("mbrtoc16 incorrectly wrote to char16_t value with "
395 		    "zero length string, found 0x%x not 0x%x", out, exp);
396 		ret = B_FALSE;
397 	}
398 
399 	return (ret);
400 }
401 
402 static boolean_t
403 mbrtoc16_roundtrip(void)
404 {
405 	char16_t out;
406 	size_t len, clen;
407 	mbstate_t mbs;
408 	char buf[MB_CUR_MAX];
409 	boolean_t ret = B_TRUE;
410 
411 	bzero(&mbs, sizeof (mbs));
412 	len = mbrtoc16(&out, uchar_wide, strlen(uchar_wide), &mbs);
413 	if (len != 3) {
414 		warnx("mbrtoc16 returned %zu, expected %u", len, 3);
415 		ret = B_FALSE;
416 	}
417 
418 	if (out != uchar_value) {
419 		warnx("mbrtoc16 converted character to 0x%x not 0x%x",
420 		    out, uchar_value);
421 		ret = B_FALSE;
422 	}
423 
424 	clen = c16rtomb(buf, out, &mbs);
425 	if (clen != len) {
426 		warnx("c16rtomb returned %d bytes, but we originally used %d",
427 		    clen, len);
428 		ret = B_FALSE;
429 	}
430 
431 	if (strncmp(buf, uchar_wide, len) != 0) {
432 		warnx("round trip string comparison failed");
433 		ret = B_FALSE;
434 	}
435 
436 	return (ret);
437 }
438 
439 static boolean_t
440 mbrtoc16_partial(void)
441 {
442 	char16_t out;
443 	size_t len, i;
444 	mbstate_t mbs;
445 	boolean_t ret = B_TRUE;
446 
447 	bzero(&mbs, sizeof (mbs));
448 	for (i = 0; i < strlen(uchar_wide) - 1; i++) {
449 		len = mbrtoc16(&out, uchar_wide + i, 1, &mbs);
450 		if (len != (size_t)-2) {
451 			warnx("partial mbrtoc16 returned %zu, not -2", len);
452 			ret = B_FALSE;
453 		}
454 	}
455 
456 	len = mbrtoc16(&out, uchar_wide + i, 1, &mbs);
457 	if (len != 1) {
458 		warnx("partial mbrtoc16 returned %zu, not 1", len);
459 		ret = B_FALSE;
460 	}
461 
462 	if (out != uchar_value) {
463 		warnx("mbrtoc16 converted character to 0x%x not 0x%x",
464 		    out, uchar_value);
465 		ret = B_FALSE;
466 	}
467 
468 	return (ret);
469 }
470 
471 static boolean_t
472 mbrtoc16_surrogate(void)
473 {
474 	char16_t out0, out1;
475 	size_t len, clen;
476 	mbstate_t mbs;
477 	const char *surrogate = "\xF0\x9F\x92\xA9";
478 	char16_t exp0 = 0xd83d, exp1 = 0xdca9;
479 	size_t slen = strlen(surrogate);
480 	boolean_t ret = B_TRUE;
481 	char buf[MB_CUR_MAX];
482 
483 	bzero(&mbs, sizeof (mbs));
484 	len = mbrtoc16(&out0, surrogate, slen, &mbs);
485 	if (len != slen) {
486 		warnx("mbrtoc16 returned %zu, expected %u", len, slen);
487 		ret = B_FALSE;
488 	}
489 
490 	if (out0 != exp0) {
491 		warnx("mbrtoc16 converted character to 0x%x not 0x%x",
492 		    out0, exp0);
493 		ret = B_FALSE;
494 	}
495 
496 	if (mbsinit(&mbs) != 0) {
497 		warnx("mb state with a surrogate character is somehow in the "
498 		    "initial state");
499 		ret = B_FALSE;
500 	}
501 
502 	len = mbrtoc16(&out1, uchar_wide, strlen(uchar_wide), &mbs);
503 	if (len != (size_t)-3) {
504 		warnx("mbrtoc16 returned %zu, expected -3", len);
505 		ret = B_FALSE;
506 	}
507 
508 	if (mbsinit(&mbs) == 0) {
509 		warnx("mb state with after both surrogate characters isn't "
510 		    "in initial state");
511 		ret = B_FALSE;
512 	}
513 
514 	if (out1 != exp1) {
515 		warnx("mbrtoc32 converted character to 0x%x not 0x%x",
516 		    out1, exp1);
517 		ret = B_FALSE;
518 	}
519 
520 	clen = c16rtomb(buf, out0, &mbs);
521 	if (clen != 0) {
522 		warnx("c16rtomb returned %d bytes, but expected zero for the "
523 		    "first surrogate", clen);
524 		ret = B_FALSE;
525 	}
526 
527 	if (mbsinit(&mbs) != 0) {
528 		warnx("mb state with a surrogate character is somehow in the "
529 		    "initial state");
530 		ret = B_FALSE;
531 	}
532 
533 	clen = c16rtomb(buf, out1, &mbs);
534 	if (clen != slen) {
535 		warnx("c16rtomb returned %zd, expected %u", len, slen);
536 		ret = B_FALSE;
537 	}
538 
539 	if (mbsinit(&mbs) == 0) {
540 		warnx("mb state with after both surrogate characters isn't "
541 		    "in initial state");
542 		ret = B_FALSE;
543 	}
544 
545 	if (strncmp(buf, surrogate, slen) != 0) {
546 		warnx("round trip string comparison failed");
547 		ret = B_FALSE;
548 	}
549 
550 	return (ret);
551 }
552 
553 static boolean_t
554 c32rtomb_eilseq_iso8859(void)
555 {
556 	char buf[MB_CUR_MAX];
557 	mbstate_t mbs;
558 	size_t len;
559 	boolean_t ret = B_TRUE;
560 
561 	bzero(&mbs, sizeof (mbs));
562 	len = c32rtomb(buf, uchar_value, &mbs);
563 	if (len != (size_t)-1) {
564 		warnx("c32rtomb returned %zd, expected -1\n", len);
565 		ret = B_FALSE;
566 	}
567 
568 	if (errno != EILSEQ) {
569 		warnx("expected errno set to %d was %d", EILSEQ, errno);
570 		ret = B_FALSE;
571 	}
572 
573 	return (ret);
574 }
575 
576 static boolean_t
577 c16rtomb_eilseq_iso8859(void)
578 {
579 	char buf[MB_CUR_MAX];
580 	mbstate_t mbs;
581 	size_t len;
582 	boolean_t ret = B_TRUE;
583 
584 	bzero(&mbs, sizeof (mbs));
585 	len = c32rtomb(buf, (char16_t)uchar_value, &mbs);
586 	if (len != (size_t)-1) {
587 		warnx("c32rtomb returned %zd, expected -1\n", len);
588 		ret = B_FALSE;
589 	}
590 
591 	if (errno != EILSEQ) {
592 		warnx("expected errno set to %d was %d", EILSEQ, errno);
593 		ret = B_FALSE;
594 	}
595 
596 	return (ret);
597 }
598 
599 static boolean_t
600 c32rtomb_eilseq_utf8(void)
601 {
602 	char buf[MB_CUR_MAX];
603 	mbstate_t mbs;
604 	size_t len;
605 	boolean_t ret = B_TRUE;
606 
607 	bzero(&mbs, sizeof (mbs));
608 	len = c32rtomb(buf, UINT32_MAX, &mbs);
609 	if (len != (size_t)-1) {
610 		warnx("c32rtomb returned %zd, expected -1\n", len);
611 		ret = B_FALSE;
612 	}
613 
614 	if (errno != EILSEQ) {
615 		warnx("expected errno set to %d was %d", EILSEQ, errno);
616 		ret = B_FALSE;
617 	}
618 
619 	return (ret);
620 }
621 
622 static boolean_t
623 c16rtomb_bad_first(void)
624 {
625 	char buf[MB_CUR_MAX];
626 	mbstate_t mbs;
627 	size_t len, i;
628 	char16_t first = 0xd83d;
629 	char16_t bad[] = { 0x0, 0xd7ff, 0xd83d, 0xd900, 0xffff };
630 	boolean_t ret = B_TRUE;
631 
632 	for (i = 0; i < ARRAY_SIZE(bad); i++) {
633 		bzero(&mbs, sizeof (mbs));
634 		len = c16rtomb(buf, first, &mbs);
635 		if (len != 0) {
636 			warnx("c16rtomb returned %zd, expected 0\n", len);
637 			ret = B_FALSE;
638 		}
639 
640 		len = c16rtomb(buf, bad[i], &mbs);
641 		if (len != (size_t)-1) {
642 			warnx("c16rtomb surrogate %x returned %zd, expected "
643 			    "-1\n", bad[i], len);
644 			ret = B_FALSE;
645 		}
646 
647 		if (errno != EILSEQ) {
648 			warnx("expected errno set to %d was %d", EILSEQ, errno);
649 			ret = B_FALSE;
650 		}
651 	}
652 
653 	return (ret);
654 }
655 
656 static boolean_t
657 c16rtomb_bad_second(void)
658 {
659 	char buf[MB_CUR_MAX];
660 	mbstate_t mbs;
661 	size_t len, i;
662 	char16_t bad[] = { 0xdc00, 0xdd34, 0xdfff };
663 	boolean_t ret = B_TRUE;
664 
665 	for (i = 0; i < ARRAY_SIZE(bad); i++) {
666 		bzero(&mbs, sizeof (mbs));
667 		len = c16rtomb(buf, bad[i], &mbs);
668 		if (len != (size_t)-1) {
669 			warnx("c16rtomb surrogate %x returned %zd, expected "
670 			    "-1\n", bad[i], len);
671 			ret = B_FALSE;
672 		}
673 
674 		if (errno != EILSEQ) {
675 			warnx("expected errno set to %d was %d", EILSEQ, errno);
676 			ret = B_FALSE;
677 		}
678 	}
679 
680 	return (ret);
681 }
682 
683 static boolean_t
684 c32rtomb_null(void)
685 {
686 	size_t len;
687 	mbstate_t mbs;
688 	boolean_t ret = B_TRUE;
689 
690 	bzero(&mbs, sizeof (mbs));
691 	len = c32rtomb(NULL, uchar_value, &mbs);
692 	if (len != 1) {
693 		warnx("c32rtomb returned %zd, expected %zd", len, 1);
694 		ret = B_FALSE;
695 	}
696 
697 	return (ret);
698 }
699 
700 static boolean_t
701 c16rtomb_null(void)
702 {
703 	size_t len;
704 	mbstate_t mbs;
705 	boolean_t ret = B_TRUE;
706 
707 	bzero(&mbs, sizeof (mbs));
708 	len = c16rtomb(NULL, uchar_value, &mbs);
709 	if (len != 1) {
710 		warnx("c16rtomb returned %zd, expected %zd", len, 1);
711 		ret = B_FALSE;
712 	}
713 
714 	return (ret);
715 }
716 
717 typedef boolean_t (*uchar_test_f)(void);
718 
719 typedef struct uchar_test {
720 	uchar_test_f	ut_func;
721 	const char	*ut_test;
722 	const char	*ut_locale;
723 } uchar_test_t;
724 
725 static const uchar_test_t uchar_tests[] = {
726 	{ mbrtoc32_ascii_mbstate, "mbrtoc32: ascii conversion" },
727 	{ mbrtoc32_ascii_internal, "mbrtoc32: ascii conversion (internal "
728 	    "mbstate_t)" },
729 	{ mbrtoc32_badseq_utf8, "mbrtoc32: bad locale sequence (UTF-8)" },
730 	{ mbrtoc32_roundtrip, "mbrtoc32: round trip conversion" },
731 	{ mbrtoc32_partial, "mbrtoc32: correctly consume partial sequences" },
732 	{ mbrtoc32_zero, "mbrtoc32: correctly handle L'\\0'" },
733 	{ mbrtoc32_zero_len, "mbrtoc32: correctly handle length of zero" },
734 	{ mbrtoc32_null, "mbrtoc32: correctly handle null string" },
735 	{ mbrtoc16_ascii_mbstate, "mbrtoc16: ascii conversion" },
736 	{ mbrtoc16_ascii_internal, "mbrtoc16: ascii conversion (internal "
737 	    "mbstate_t)" },
738 	{ mbrtoc16_null, "mbrtoc16: correctly handle null string" },
739 	{ mbrtoc16_zero, "mbrtoc16: correctly handle L'\\0'" },
740 	{ mbrtoc16_zero_len, "mbrtoc16: correctly handle length of zero" },
741 	{ mbrtoc16_roundtrip, "mbrtoc16: round trip conversion" },
742 	{ mbrtoc16_partial, "mbrtoc16: correctly consume partial sequences" },
743 	{ mbrtoc16_surrogate, "mbrtoc16: correctly generate surrogate pairs "
744 	    "and round trip conversion" },
745 	{ c32rtomb_eilseq_iso8859, "c32rtomb: character outside of locale is "
746 	    "caught", "en_US.ISO8859-1" },
747 	{ c16rtomb_eilseq_iso8859, "c16rtomb: character outside of locale is "
748 	    "caught", "en_US.ISO8859-1" },
749 	{ c32rtomb_eilseq_utf8, "c32rtomb: character outside of locale is "
750 	    "caught" },
751 	{ c16rtomb_bad_first, "c16rtomb: bad first surrogate pair" },
752 	{ c16rtomb_bad_second, "c16rtomb: bad second surrogate pair" },
753 	{ c32rtomb_null, "c32rtomb: correctly handle null buffer" },
754 	{ c16rtomb_null, "c16rtomb: correctly handle null buffer" },
755 };
756 
757 int
758 main(void)
759 {
760 	uint_t i;
761 	uint_t passes = 0;
762 	uint_t ntests = ARRAY_SIZE(uchar_tests);
763 
764 	for (i = 0; i < ntests; i++) {
765 		boolean_t r;
766 
767 		/*
768 		 * Default to a standard UTF-8 locale if none is requested by
769 		 * the test.
770 		 */
771 		if (uchar_tests[i].ut_locale != NULL) {
772 			update_locale(uchar_tests[i].ut_locale);
773 		} else {
774 			update_locale("en_US.UTF-8");
775 		}
776 
777 		r = uchar_tests[i].ut_func();
778 		(void) fprintf(stderr, "TEST %s: %s\n", r ? "PASSED" : "FAILED",
779 		    uchar_tests[i].ut_test);
780 		if (r) {
781 			passes++;
782 		}
783 	}
784 
785 	(void) printf("%d/%d test%s passed\n", passes, ntests,
786 	    passes > 1 ? "s" : "");
787 	return (passes == ntests ? EXIT_SUCCESS : EXIT_FAILURE);
788 
789 }
790