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 #include <note.h>
33 #include "test_common.h"
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 int extra_debug = 0;
57 
58 void
testlocale_thr_one(test_t t,void * arg)59 testlocale_thr_one(test_t t, void *arg)
60 {
61 	_NOTE(ARGUNUSED(arg));
62 	locale_t	cloc, loc;
63 	struct lconv	*lc;
64 	char		*day;
65 
66 	for (int i = 0; i < NUMITR; i++) {
67 		struct ldata *l = &ldata[i % NUM_LDATA];
68 		cloc = uselocale(NULL);
69 
70 		loc = newlocale(LC_ALL_MASK, l->locale, NULL);
71 		if (loc == NULL) {
72 			test_failed(t, "newlocale %s failed", l->locale);
73 		}
74 		day = nl_langinfo_l(DAY_1, loc);
75 		if (strcmp(day, l->day1) != 0) {
76 			test_failed(t, "newlocale data mismatch (%s != %s)",
77 			    day, l->day1);
78 		}
79 		if (extra_debug)
80 			test_debugf(t, "DAY1: %s", day);
81 
82 		day = nl_langinfo(DAY_1);
83 		if (strcmp(day, "Sunday") != 0) {
84 			test_failed(t, "C locale day wrong %s != Sunday",
85 			    day);
86 		}
87 		lc = localeconv();
88 		if (strcmp(lc->currency_symbol, "") != 0) {
89 			test_failed(t, "C cursym mismatch (%s != %s)",
90 			    lc->currency_symbol, "");
91 		}
92 
93 		/* we sleep a random bit to mix it up */
94 		(void) usleep(rand() % 10);
95 
96 		(void) uselocale(loc);
97 		day = nl_langinfo(DAY_1);
98 		if (strcmp(day, l->day1) != 0) {
99 			test_failed(t, "uselocale data mismatch (%s != %s)",
100 			    day, l->day1);
101 		}
102 
103 		lc = localeconv();
104 		if (strcmp(lc->currency_symbol, l->cursym) != 0) {
105 			test_failed(t, "uselocal cursym %s != %s",
106 			    lc->currency_symbol, l->cursym);
107 		}
108 		if (extra_debug)
109 			test_debugf(t, "CSYM: %s", lc->currency_symbol);
110 
111 		/* we sleep a random bit to mix it up */
112 		(void) usleep(rand() % 10);
113 
114 		if (uselocale(cloc) != loc) {
115 			test_failed(t, "revert old locale mismatch");
116 		}
117 		freelocale(loc);
118 		if (uselocale(LC_GLOBAL_LOCALE) != cloc) {
119 			test_failed(t, "revert GLOBAL_LOCALE mismatch");
120 		}
121 	}
122 	test_passed(t);
123 }
124 
125 
126 void
test_newlocale_threaded(void)127 test_newlocale_threaded(void)
128 {
129 	test_run(NUMTHR, testlocale_thr_one, NULL, "newlocale_threaded");
130 }
131 
132 void
test_newlocale_negative(void)133 test_newlocale_negative(void)
134 {
135 	locale_t loc, bad;
136 	char *day;
137 	char *tname = "newlocale_negative";
138 	test_t t;
139 
140 	t = test_start(tname);
141 	loc = newlocale(LC_ALL_MASK, "de_DE.UTF-8", NULL);
142 	if (loc == NULL) {
143 		test_failed(t, "cannot set de_DE.UTF-8");
144 	}
145 	day = nl_langinfo_l(DAY_1, loc);
146 	if (strcmp(day, "Sonntag") != 0) {
147 		test_failed(t, "incorrect Sonntag != %s", day);
148 	}
149 
150 	bad = newlocale(LC_ALL_MASK, "cn_US.BIZRRE", loc);
151 	if (bad != NULL) {
152 		test_failed(t, "passed setting bogus locale");
153 	}
154 	day = nl_langinfo_l(DAY_1, loc);
155 	if (strcmp(day, "Sonntag") != 0) {
156 		test_failed(t, "incorrect Sonntag != %s", day);
157 	}
158 	test_passed(t);
159 }
160 
161 void
test_newlocale_categories(void)162 test_newlocale_categories(void)
163 {
164 	locale_t loc;
165 	char *day, *cur, *yes;
166 	char *tname = "newlocale_categories";
167 	test_t t;
168 
169 	t = test_start(tname);
170 
171 	loc = NULL;
172 	loc = newlocale(LC_TIME_MASK, "de_DE.UTF-8", loc);
173 	loc = newlocale(LC_MESSAGES_MASK, "ru_RU.UTF-8", loc);
174 	loc = newlocale(LC_MONETARY_MASK, "en_US.UTF-8", loc);
175 
176 	if (loc == NULL) {
177 		test_failed(t, "failed to set locale");
178 	}
179 
180 	day = nl_langinfo_l(DAY_1, loc);
181 	if ((day == NULL) || (strcmp(day, "Sonntag") != 0)) {
182 		test_failed(t, "day1 mismatch %s != %s", day, "Sonntag");
183 	}
184 	yes = nl_langinfo_l(YESSTR, loc);
185 	if ((yes == NULL) || (strcmp(yes, "да") != 0)) {
186 		test_failed(t, "currency mismatch");
187 	}
188 	cur = nl_langinfo_l(CRNCYSTR, loc);
189 	if ((cur == NULL) || (strcmp(cur, "-$") != 0)) {
190 		test_failed(t, "currency mismatch [%s] != [%s]", cur, "-$");
191 	}
192 
193 	test_passed(t);
194 }
195 
196 void
test_newlocale_composite(void)197 test_newlocale_composite(void)
198 {
199 	locale_t loc;
200 	char *day, *cur, *yes;
201 	char *tname = "newlocale_composite";
202 	test_t t;
203 
204 	t = test_start(tname);
205 
206 	/* order: CTYPE/NUMERIC/TIME/COLLATE/MONETARY/MESSAGES */
207 	loc = newlocale(LC_ALL_MASK,
208 	    "C/C/de_DE.UTF-8/C/en_US.UTF-8/ru_RU.UTF-8", NULL);
209 
210 	if (loc == NULL) {
211 		test_failed(t, "failed to set composite locale");
212 	}
213 
214 	day = nl_langinfo_l(DAY_1, loc);
215 	if ((day == NULL) || (strcmp(day, "Sonntag") != 0)) {
216 		test_failed(t, "day1 mismatch %s != %s", day, "Sonntag");
217 	}
218 	yes = nl_langinfo_l(YESSTR, loc);
219 	if ((yes == NULL) || (strcmp(yes, "да") != 0)) {
220 		test_failed(t, "currency mismatch");
221 	}
222 	cur = nl_langinfo_l(CRNCYSTR, loc);
223 	if ((cur == NULL) || (strcmp(cur, "-$") != 0)) {
224 		test_failed(t, "currency mismatch [%s] != [%s]", cur, "-$");
225 	}
226 
227 	test_passed(t);
228 }
229 
230 int
main(int argc,char ** argv)231 main(int argc, char **argv)
232 {
233 	int optc;
234 
235 	while ((optc = getopt(argc, argv, "Ddf")) != EOF) {
236 		switch (optc) {
237 		case 'd':
238 			test_set_debug();
239 			break;
240 		case 'f':
241 			test_set_force();
242 			break;
243 		case 'D':
244 			test_set_debug();
245 			extra_debug++;
246 			break;
247 		default:
248 			(void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
249 			exit(1);
250 		}
251 	}
252 
253 	test_newlocale_threaded();
254 	test_newlocale_negative();
255 	test_newlocale_categories();
256 	test_newlocale_composite();
257 
258 	exit(0);
259 }
260