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 }