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 2014 Garrett D'Amore <garrett@damore.org> 14 */ 15 16 /* 17 * This program tests that newlocale and uselocale work properly in 18 * multi-threaded programs. In order for it to work, it requires that 19 * some additional locales be installed. 20 */ 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <locale.h> 26 #include <libintl.h> 27 #include <langinfo.h> 28 #include <nl_types.h> 29 #include <err.h> 30 #include <unistd.h> 31 #include <pthread.h> 32 33 int debug = 0; 34 35 /* 36 * Note that on some platforms, different symbols are used. For example, 37 * MacOS Mavericks uses "Eu" for Euro symbol, instead of €. If the locale 38 * data changes, then this program will need to update to reflect that. 39 */ 40 struct ldata { 41 const char *locale; 42 const char *day1; 43 const char *cursym; 44 } ldata[] = { 45 { "C", "Sunday", "" }, 46 { "en_US.UTF-8", "Sunday", "$" }, 47 { "de_DE.UTF-8", "Sonntag", "€" }, 48 { "ru_RU.UTF-8", "воскресенье", "руб." }, 49 { "ja_JP.UTF-8", "日曜日", "¥" }, 50 }; 51 52 #define NUM_LDATA 5 53 #define NUMTHR 20 54 #define NUMITR 200 55 56 static void 57 test_start(const char *testName, const char *format, ...) 58 { 59 va_list args; 60 61 (void) printf("TEST STARTING %s (%s): ", testName, ARCH); 62 63 va_start(args, format); 64 (void) vprintf(format, args); 65 va_end(args); 66 (void) fflush(stdout); 67 } 68 69 static void 70 test_failed(const char *testName, const char *format, ...) 71 { 72 va_list args; 73 74 (void) printf("TEST FAILED %s (%s): ", testName, ARCH); 75 76 va_start(args, format); 77 (void) vprintf(format, args); 78 va_end(args); 79 (void) printf("\n"); 80 81 (void) exit(-1); 82 } 83 84 static void 85 test_passed(const char *testName) 86 { 87 (void) printf("TEST PASS: %s (%s)\n", testName, ARCH); 88 (void) fflush(stdout); 89 } 90 91 void * 92 testlocale_thr_one(void *ptr) 93 { 94 locale_t cloc, loc; 95 struct lconv *lc; 96 char *day; 97 char *tname = ptr; 98 99 for (int i = 0; i < NUMITR; i++) { 100 struct ldata *l = &ldata[i % NUM_LDATA]; 101 cloc = uselocale(NULL); 102 103 loc = newlocale(LC_ALL_MASK, l->locale, NULL); 104 if (loc == NULL) { 105 test_failed("newlocale %s failed", l->locale); 106 } 107 day = nl_langinfo_l(DAY_1, loc); 108 if (strcmp(day, l->day1) != 0) { 109 test_failed(tname, "newlocale data mismatch (%s != %s)", 110 day, l->day1); 111 } 112 if (debug) 113 (void) printf("DAY1: %s\n", day); 114 115 day = nl_langinfo(DAY_1); 116 if (strcmp(day, "Sunday") != 0) { 117 test_failed(tname, "C locale day wrong %s != Sunday", 118 day); 119 } 120 lc = localeconv(); 121 if (strcmp(lc->currency_symbol, "") != 0) { 122 test_failed(tname, "C cursym mismatch (%s != %s)", 123 lc->currency_symbol, ""); 124 } 125 126 /* we sleep a random bit to mix it up */ 127 (void) usleep(rand() % 10); 128 129 (void) uselocale(loc); 130 day = nl_langinfo(DAY_1); 131 if (strcmp(day, l->day1) != 0) { 132 test_failed(tname, "uselocale data mismatch (%s != %s)", 133 day, l->day1); 134 } 135 136 lc = localeconv(); 137 if (strcmp(lc->currency_symbol, l->cursym) != 0) { 138 test_failed(tname, "uselocal cursym %s != %s", 139 lc->currency_symbol, l->cursym); 140 } 141 if (debug) 142 (void) printf("CSYM: %s\n", lc->currency_symbol); 143 144 /* we sleep a random bit to mix it up */ 145 (void) usleep(rand() % 10); 146 147 if (uselocale(cloc) != loc) { 148 test_failed(tname, "revert old locale mismatch"); 149 } 150 freelocale(loc); 151 if (uselocale(LC_GLOBAL_LOCALE) != cloc) { 152 test_failed(tname, "revert GLOBAL_LOCALE mismatch"); 153 } 154 } 155 return (NULL); 156 } 157 158 159 void 160 test_newlocale_threaded(void) 161 { 162 char *tname = "newlocale_threaded"; 163 pthread_t tid[NUMTHR]; 164 165 test_start(tname, "running %d threads %d iterations\n", NUMTHR, NUMITR); 166 167 for (int i = 0; i < NUMTHR; i++) { 168 (void) pthread_create(&tid[i], NULL, testlocale_thr_one, tname); 169 } 170 171 for (int i = 0; i < NUMTHR; i++) { 172 (void) pthread_join(tid[i], NULL); 173 } 174 test_passed(tname); 175 } 176 177 void 178 test_newlocale_negative(void) 179 { 180 181 locale_t loc, bad; 182 char *day; 183 char *tname = "newlocale_negative"; 184 185 test_start(tname, "verify proper failure handling\n"); 186 loc = newlocale(LC_ALL_MASK, "de_DE.UTF-8", NULL); 187 if (loc == NULL) { 188 test_failed(tname, "cannot set de_DE.UTF-8"); 189 } 190 day = nl_langinfo_l(DAY_1, loc); 191 if (strcmp(day, "Sonntag") != 0) { 192 test_failed(tname, "incorrect Sonntag != %s", day); 193 } 194 195 bad = newlocale(LC_ALL_MASK, "cn_US.BIZRRE", loc); 196 if (bad != NULL) { 197 test_failed(tname, "passed setting bogus locale"); 198 } 199 day = nl_langinfo_l(DAY_1, loc); 200 if (strcmp(day, "Sonntag") != 0) { 201 test_failed(tname, "incorrect Sonntag != %s", day); 202 } 203 test_passed(tname); 204 } 205 206 void 207 test_newlocale_categories(void) 208 { 209 locale_t loc; 210 char *day, *cur, *yes; 211 char *tname = "newlocale_categories"; 212 213 test_start(tname, "verify different category behavior\n"); 214 215 loc = NULL; 216 loc = newlocale(LC_TIME_MASK, "de_DE.UTF-8", loc); 217 loc = newlocale(LC_MESSAGES_MASK, "ru_RU.UTF-8", loc); 218 loc = newlocale(LC_MONETARY_MASK, "en_US.UTF-8", loc); 219 220 if (loc == NULL) { 221 test_failed(tname, "failed to set locale"); 222 } 223 224 day = nl_langinfo_l(DAY_1, loc); 225 if ((day == NULL) || (strcmp(day, "Sonntag") != 0)) { 226 test_failed(tname, "day1 mismatch %s != %s", day, "Sonntag"); 227 } 228 yes = nl_langinfo_l(YESSTR, loc); 229 if ((yes == NULL) || (strcmp(yes, "да") != 0)) { 230 test_failed(tname, "currency mismatch"); 231 } 232 cur = nl_langinfo_l(CRNCYSTR, loc); 233 if ((cur == NULL) || (strcmp(cur, "-$") != 0)) { 234 test_failed(tname, "currency mismatch [%s] != [%s]", cur, "-$"); 235 } 236 237 test_passed(tname); 238 } 239 240 void 241 test_newlocale_composite(void) 242 { 243 locale_t loc; 244 char *day, *cur, *yes; 245 char *tname = "newlocale_composite"; 246 247 test_start(tname, "verify composite locale behavior\n"); 248 249 /* order: CTYPE/NUMERIC/TIME/COLLATE/MONETARY/MESSAGES */ 250 loc = newlocale(LC_ALL_MASK, 251 "C/C/de_DE.UTF-8/C/en_US.UTF-8/ru_RU.UTF-8", NULL); 252 253 if (loc == NULL) { 254 test_failed(tname, "failed to set composite locale"); 255 } 256 257 day = nl_langinfo_l(DAY_1, loc); 258 if ((day == NULL) || (strcmp(day, "Sonntag") != 0)) { 259 test_failed(tname, "day1 mismatch %s != %s", day, "Sonntag"); 260 } 261 yes = nl_langinfo_l(YESSTR, loc); 262 if ((yes == NULL) || (strcmp(yes, "да") != 0)) { 263 test_failed(tname, "currency mismatch"); 264 } 265 cur = nl_langinfo_l(CRNCYSTR, loc); 266 if ((cur == NULL) || (strcmp(cur, "-$") != 0)) { 267 test_failed(tname, "currency mismatch [%s] != [%s]", cur, "-$"); 268 } 269 270 test_passed(tname); 271 } 272 273 int 274 main(int argc, char **argv) 275 { 276 int optc; 277 278 while ((optc = getopt(argc, argv, "d")) != EOF) { 279 switch (optc) { 280 case 'd': 281 debug++; 282 break; 283 default: 284 (void) fprintf(stderr, "Usage: %s [-d]\n", argv[0]); 285 exit(1); 286 } 287 } 288 289 test_newlocale_threaded(); 290 test_newlocale_negative(); 291 test_newlocale_categories(); 292 test_newlocale_composite(); 293 294 exit(0); 295 }