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 2025 Oxide Computer Company 14 * Copyright 2025 Bill Sommerfeld 15 */ 16 17 /* 18 * Tests for modern wcsftime (XPG5 and later) and wcsftime_l. The libc tests 19 * depend on locale/ar, locale/de, locale/en, and locale/ja. We limit 20 * ourselves to these locales, plus C.UTF-8. 21 */ 22 23 #include <err.h> 24 #include <stdlib.h> 25 #include <xlocale.h> 26 #include <locale.h> 27 #include <sys/sysmacros.h> 28 #include <sys/debug.h> 29 #include <stdbool.h> 30 #include <string.h> 31 32 33 /* 34 * We're just testing that the desired locale reaches the underlying 35 * formatter so we only look at one attribute: the full month name. 36 */ 37 static const struct test_locale { 38 const char *name; 39 const char *monthname; 40 } locales[] = { 41 { "C.UTF-8", "December"}, 42 {"ja_JP.UTF-8", "12月"}, 43 {"de_DE.UTF-8", "Dezember"}, 44 {"en_US.UTF-8", "December"}, 45 }; 46 47 struct tm sample_tm = { .tm_mon = 11 }; 48 49 #define WCSSIZE 100 50 #define CSIZE 200 51 52 /* 53 * Test that the correct decimal point is recognized. 54 */ 55 bool 56 test_locale(const char *name, const char *monthname) 57 { 58 bool result_ok = true; 59 wchar_t wfmt[WCSSIZE]; 60 wchar_t wcs[WCSSIZE]; 61 char cstring[CSIZE]; 62 63 size_t len; 64 65 locale_t loc = newlocale(LC_ALL_MASK, name, NULL); 66 67 if (loc == NULL) { 68 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to " 69 "construct locale %s", name); 70 } 71 72 if (mbstowcs_l(wfmt, "%B", WCSSIZE, loc) == (size_t)-1) { 73 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to " 74 "construct wide char format string"); 75 } 76 77 len = wcsftime_l(wcs, WCSSIZE, wfmt, &sample_tm, loc); 78 if (len == (size_t)-1) { 79 warn("wcsftime_l returned -1"); 80 result_ok = false; 81 } 82 83 if (wcstombs_l(cstring, wcs, CSIZE, loc) == (size_t)-1) { 84 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to " 85 "convert wide char string back to multibyte string"); 86 } 87 88 if (strcmp(cstring, monthname)) { 89 warn("Wrong monthname for locale %s month %d: " 90 "got %s expected %s", name, sample_tm.tm_mon+1, 91 cstring, monthname); 92 result_ok = false; 93 } 94 95 (void) memset(wcs, 0, sizeof (wcs)); 96 (void) memset(cstring, 0, sizeof (cstring)); 97 98 if (uselocale(loc) == NULL) { 99 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to " 100 "set locale %s", name); 101 } 102 103 len = wcsftime(wcs, WCSSIZE, wfmt, &sample_tm); 104 if (len == (size_t)-1) { 105 warn("wcsftime_l returned -1"); 106 result_ok = false; 107 } 108 109 if (wcstombs(cstring, wcs, CSIZE) == (size_t)-1) { 110 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to " 111 "convert wide char string back to multibyte string"); 112 } 113 114 if (strcmp(cstring, monthname)) { 115 warn("Wrong monthname for locale %s month %d: " 116 "got %s expected %s", name, sample_tm.tm_mon+1, 117 cstring, monthname); 118 result_ok = false; 119 } 120 121 return (result_ok); 122 } 123 124 125 int 126 main(void) 127 { 128 int ret = EXIT_SUCCESS; 129 130 for (size_t i = 0; i < ARRAY_SIZE(locales); i++) { 131 if (!test_locale(locales[i].name, locales[i].monthname)) 132 ret = EXIT_FAILURE; 133 } 134 135 if (ret == EXIT_SUCCESS) { 136 (void) printf("All tests completed successfully\n"); 137 } 138 139 return (ret); 140 } 141