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 */
40struct 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
56int extra_debug = 0;
57
58void
59testlocale_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
126void
127test_newlocale_threaded(void)
128{
129	test_run(NUMTHR, testlocale_thr_one, NULL, "newlocale_threaded");
130}
131
132void
133test_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
161void
162test_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
196void
197test_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
230int
231main(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