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 wcsrtombs and friends work properly.
18 * In order for it to work, it requires that some additional locales
19 * be installed.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <locale.h>
26 #include <wchar.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <xlocale.h>
31 #include <note.h>
32 #include "test_common.h"
33
34 int extra_debug = 0;
35
36 #define NUMTHR 300
37 #define NUMITR 300
38
39 /*
40 * Note that this file is easiest edited with a UTF-8 capable editor,
41 * as there are embedded UTF-8 symbols in some of the strings.
42 */
43 struct wcsrtombs_test {
44 char mbs[32];
45 wchar_t wcs[32];
46 };
47
48 #define TESTING_MBS "TESTING"
49 #define TESTING_WCS { 'T', 'E', 'S', 'T', 'I', 'N', 'G', 0 }
50 #define HELLO_RU_MBS "ПРИВЕТ"
51 #define HELLO_RU_WCS { 1055, 1056, 1048, 1042, 1045, 1058, 0 }
52 #define HELLO_EN_MBS "HELLO"
53 #define HELLO_EN_WCS { 'H', 'E', 'L', 'L', 'O', 0 }
54
55 /* Unicode values never have the high order bit set */
56 #define BAD_WCS { 'B', 'A', 'D', (wchar_t)0xf000f000, 'W', 'C', 'S' }
57
58 struct wcsrtombs_test C_data[] = {
59 { TESTING_MBS, TESTING_WCS },
60 { HELLO_EN_MBS, HELLO_EN_WCS },
61 { 0, 0 },
62 };
63
64 struct wcsrtombs_test utf8_data[] = {
65 { TESTING_MBS, TESTING_WCS },
66 { HELLO_EN_MBS, HELLO_EN_WCS },
67 { HELLO_RU_MBS, HELLO_RU_WCS },
68 { 0, 0 },
69 };
70
71 struct {
72 const char *locale;
73 struct wcsrtombs_test *test;
74 } locales[] = {
75 { "C", C_data },
76 { "en_US.UTF-8", utf8_data },
77 { NULL, NULL }
78 };
79
80 void
test_wcsrtombs_1(const char * locale,struct wcsrtombs_test * test)81 test_wcsrtombs_1(const char *locale, struct wcsrtombs_test *test)
82 {
83 test_t t;
84 char *v;
85 mbstate_t ms;
86
87 t = test_start("wcsrtombs (locale %s)", locale);
88
89 v = setlocale(LC_ALL, locale);
90 if (v == NULL) {
91 test_failed(t, "setlocale failed: %s", strerror(errno));
92 }
93 if (strcmp(v, locale) != 0) {
94 test_failed(t, "setlocale got %s instead of %s", v, locale);
95 }
96
97 for (int i = 0; test[i].mbs[0] != 0; i++) {
98 char mbs[32];
99 const wchar_t *wcs = test[i].wcs;
100 size_t cnt;
101
102 (void) memset(&ms, 0, sizeof (ms));
103 (void) memset(mbs, 0, sizeof (mbs));
104 cnt = wcsrtombs(mbs, &wcs, sizeof (mbs), &ms);
105 if (cnt != strlen(test[i].mbs)) {
106 test_failed(t, "incorrect return value: %d != %d",
107 cnt, strlen(test[i].mbs));
108 }
109 if (strcmp(mbs, test[i].mbs) != 0) {
110 test_failed(t, "wrong result: %s != %s",
111 mbs, test[i].mbs);
112 }
113 if (extra_debug) {
114 test_debugf(t, "mbs is %s", mbs);
115 }
116 }
117 test_passed(t);
118 }
119
120 void
test_wcsrtombs_l(const char * locale,struct wcsrtombs_test * test)121 test_wcsrtombs_l(const char *locale, struct wcsrtombs_test *test)
122 {
123 test_t t;
124 locale_t loc;
125 char *v;
126 mbstate_t ms;
127
128 t = test_start("wcsrtombs_l (locale %s)", locale);
129
130 v = setlocale(LC_ALL, "C");
131 if (v == NULL) {
132 test_failed(t, "setlocale failed: %s", strerror(errno));
133 }
134 if (strcmp(v, "C") != 0) {
135 test_failed(t, "setlocale got %s instead of %s", v, "C");
136 }
137
138 loc = newlocale(LC_ALL_MASK, locale, NULL);
139 if (loc == NULL) {
140 test_failed(t, "newlocale failed: %s", strerror(errno));
141 }
142
143 for (int i = 0; test[i].mbs[0] != 0; i++) {
144 char mbs[32];
145 const wchar_t *wcs = test[i].wcs;
146 size_t cnt;
147
148 (void) memset(&ms, 0, sizeof (ms));
149 (void) memset(mbs, 0, sizeof (mbs));
150 cnt = wcsrtombs_l(mbs, &wcs, sizeof (mbs), &ms, loc);
151 if (cnt != strlen(test[i].mbs)) {
152 test_failed(t, "incorrect return value: %d != %d",
153 cnt, strlen(test[i].mbs));
154 }
155 if (strcmp(mbs, test[i].mbs) != 0) {
156 test_failed(t, "wrong result: %s != %s", mbs,
157 test[i].mbs);
158 }
159 if (extra_debug) {
160 test_debugf(t, "mbs is %s", mbs);
161 }
162 }
163 test_passed(t);
164 }
165
166 void
test_wcsrtombs_thr_iter(test_t t,const char * locale,struct wcsrtombs_test * test)167 test_wcsrtombs_thr_iter(test_t t, const char *locale,
168 struct wcsrtombs_test *test)
169 {
170 locale_t loc;
171 mbstate_t ms;
172
173 loc = newlocale(LC_ALL_MASK, locale, NULL);
174 if (loc == NULL) {
175 test_failed(t, "newlocale failed: %s", strerror(errno));
176 }
177
178 for (int i = 0; test[i].mbs[0] != 0; i++) {
179 char mbs[32];
180 const wchar_t *wcs = test[i].wcs;
181 size_t cnt;
182
183 (void) memset(&ms, 0, sizeof (ms));
184 (void) memset(mbs, 0, sizeof (mbs));
185 cnt = wcsrtombs_l(mbs, &wcs, sizeof (mbs), &ms, loc);
186 if (cnt != strlen(test[i].mbs)) {
187 test_failed(t, "incorrect return value: %d != %d",
188 cnt, strlen(test[i].mbs));
189 }
190 if (strcmp(mbs, test[i].mbs) != 0) {
191 test_failed(t, "wrong result: %s != %s", mbs,
192 test[i].mbs);
193 }
194 if (extra_debug) {
195 test_debugf(t, "mbs is %s", mbs);
196 }
197 }
198
199 freelocale(loc);
200 }
201
202 void
test_wcsrtombs_thr_work(test_t t,void * arg)203 test_wcsrtombs_thr_work(test_t t, void *arg)
204 {
205 _NOTE(ARGUNUSED(arg));
206 for (int j = 0; j < NUMITR; j++) {
207 test_debugf(t, "iteration %d", j);
208 for (int i = 0; locales[i].locale != NULL; i++) {
209 test_wcsrtombs_thr_iter(t, locales[i].locale,
210 locales[i].test);
211 }
212 }
213 test_passed(t);
214 }
215
216 void
test_wcsrtombs_threaded(void)217 test_wcsrtombs_threaded(void)
218 {
219 (void) setlocale(LC_ALL, "C");
220 test_run(NUMTHR, test_wcsrtombs_thr_work, NULL, "wcsrtombs_threaded");
221 }
222
223 void
test_wcsrtombs_partial(void)224 test_wcsrtombs_partial(void)
225 {
226 test_t t;
227 mbstate_t ms;
228 wchar_t src[32] = HELLO_RU_WCS;
229 char mbs[32];
230 char *dst;
231 const wchar_t *wcs;
232 size_t cnt;
233
234
235 (void) memset(&ms, 0, sizeof (ms));
236 t = test_start("wcsrtombs_partial");
237
238 if (setlocale(LC_ALL, "ru_RU.UTF-8") == NULL) {
239 test_failed(t, "setlocale failed: %s", strerror(errno));
240 }
241
242 wcs = src;
243 dst = mbs;
244 cnt = wcsrtombs(dst, &wcs, 1, &ms);
245 if (cnt != 0) {
246 test_failed(t, "gave back a conversion cnt %d != 0", cnt);
247 }
248 if (wcs != src) {
249 test_failed(t, "incorrectly advanced wcs");
250 }
251
252 cnt = wcsrtombs(dst, &wcs, 2, &ms);
253 if (cnt != 2) {
254 test_failed(t, "gave back a conversion cnt %d != 2", cnt);
255 }
256 dst += cnt;
257
258 cnt = wcsrtombs(dst, &wcs, 4, &ms);
259 dst += cnt;
260
261 cnt = wcsrtombs(dst, &wcs, sizeof (mbs) - strlen(mbs), &ms);
262 if (extra_debug) {
263 test_debugf(t, "mbs is %s", mbs);
264 }
265 if (strcmp(mbs, HELLO_RU_MBS) != 0) {
266 test_failed(t, "wrong result: %s != %s", mbs, HELLO_RU_MBS);
267 }
268 test_passed(t);
269 }
270
271 void
test_wcsrtombs_negative(void)272 test_wcsrtombs_negative(void)
273 {
274 mbstate_t ms;
275 const wchar_t *wcs;
276 char mbs[32];
277 char *dst;
278 int e;
279 wchar_t src[32] = BAD_WCS;
280 test_t t;
281 int cnt;
282
283 t = test_start("wcsrtombs_negative");
284
285 (void) memset(&ms, 0, sizeof (ms));
286 if (setlocale(LC_ALL, "ru_RU.UTF-8") == NULL) {
287 test_failed(t, "setlocale failed: %s", strerror(errno));
288 }
289
290 wcs = src;
291 dst = mbs;
292 cnt = wcsrtombs(dst, &wcs, sizeof (mbs), &ms);
293 if (cnt != -1) {
294 test_failed(t, "bogus success (%d)", cnt);
295 }
296 if ((e = errno) != EILSEQ) {
297 test_failed(t, "wrong errno, wanted %d (EILSEQ), got %d: %s",
298 EILSEQ, e, strerror(e));
299 }
300 test_passed(t);
301 }
302
303 void
test_wcsnrtombs_partial(void)304 test_wcsnrtombs_partial(void)
305 {
306 test_t t;
307 mbstate_t ms;
308 wchar_t src[32] = HELLO_RU_WCS;
309 char mbs[32];
310 char *dst;
311 const wchar_t *wcs;
312 size_t cnt;
313
314
315 (void) memset(&ms, 0, sizeof (ms));
316 t = test_start("wcsrntombs_partial");
317
318 if (setlocale(LC_ALL, "ru_RU.UTF-8") == NULL) {
319 test_failed(t, "setlocale failed: %s", strerror(errno));
320 }
321
322 wcs = src;
323 dst = mbs;
324 cnt = wcsnrtombs(dst, &wcs, 1, 1, &ms);
325 if (cnt != 0) {
326 test_failed(t, "gave back a conversion cnt %d != 0", cnt);
327 }
328 if (wcs != src) {
329 test_failed(t, "incorrectly advanced wcs");
330 }
331
332 /* we should get just 2 wide characters (expanding to 4 bytes) */
333 cnt = wcsnrtombs(dst, &wcs, 2, sizeof (mbs), &ms);
334 if (cnt != 4) {
335 test_failed(t, "gave back a conversion cnt %d != 4", cnt);
336 }
337 dst += cnt;
338
339 cnt = wcsnrtombs(dst, &wcs, 32, sizeof (mbs) - strlen(mbs), &ms);
340 if (extra_debug) {
341 test_debugf(t, "mbs is %s", mbs);
342 }
343 if (strcmp(mbs, HELLO_RU_MBS) != 0) {
344 test_failed(t, "wrong result: %s != %s", mbs, HELLO_RU_MBS);
345 }
346 test_passed(t);
347 }
348
349 void
test_wcsrtombs(void)350 test_wcsrtombs(void)
351 {
352 for (int i = 0; locales[i].locale != NULL; i++) {
353 test_wcsrtombs_1(locales[i].locale, locales[i].test);
354 test_wcsrtombs_l(locales[i].locale, locales[i].test);
355 }
356 }
357
358 int
main(int argc,char ** argv)359 main(int argc, char **argv)
360 {
361 int optc;
362
363 while ((optc = getopt(argc, argv, "dfD")) != EOF) {
364 switch (optc) {
365 case 'd':
366 test_set_debug();
367 break;
368 case 'f':
369 test_set_force();
370 break;
371 case 'D':
372 test_set_debug();
373 extra_debug++;
374 break;
375 default:
376 (void) fprintf(stderr, "Usage: %s [-dfD]\n", argv[0]);
377 exit(1);
378 }
379 }
380
381 test_wcsrtombs();
382 test_wcsrtombs_partial();
383 test_wcsrtombs_negative();
384 test_wcsrtombs_threaded();
385 test_wcsnrtombs_partial();
386
387 exit(0);
388 }
389