2 * PROJECT: ReactOS wcstombs Test Suite
3 * LICENSE: GPL v2 or any later version
4 * FILE: tests/wcstombs-tests/wcstombs-tests.c
5 * PURPOSE: Application for testing the CRT API's (wcstombs and wctomb) and the Win32 API WideCharToMultiByte for the Unicode to MultiByte string conversion
6 * COPYRIGHT: Copyright 2008 Colin Finck <colin@reactos.org>
15 /* Macros for simplification */
16 #define SETLOCALE(locale) \
17 loc = setlocale(LC_ALL, locale); \
20 puts("setlocale failed for " locale ", this locale is probably not installed on your system"); \
24 #define OK(condition, fail_message, ...) \
26 printf("%d: " fail_message "\n", __LINE__, ##__VA_ARGS__);
28 /* Global variables for easier handling */
32 wchar_t wc1
= 228; /* Western Windows-1252 character */
33 wchar_t wc2
= 1088; /* Russian Windows-1251 character not displayable for Windows-1252 */
34 wchar_t wcs
[5] = {'T', 'h', 1088, 'i', 0}; /* String with ASCII characters and a Russian character */
35 wchar_t dbwcs
[3] = {28953, 25152, 0}; /* String with Chinese (codepage 950) characters */
45 /* Current locale is "C", wcstombs should return the length of the input buffer without the terminating null character */
46 ret
= wcstombs(NULL
, dbwcs
, 0);
47 OK(ret
== 2, "ret is %d", ret
);
49 ret
= wcstombs(mbs
, dbwcs
, ret
);
50 OK(ret
== -1, "ret is %d", ret
);
51 OK(mbs
[0] == 0, "mbs[0] is %d", mbs
[0]);
52 OK(errno
== EILSEQ
, "errno is %d", errno
);
54 ret
= wcstombs(NULL
, wcs
, 0);
55 OK(ret
== 4, "ret is %d", ret
);
57 ret
= wcstombs(mbs
, wcs
, ret
);
58 OK(ret
== -1, "ret is %d", ret
);
59 OK(!strcmp(mbs
, "Th"), "mbs is %s", mbs
);
60 OK(errno
== EILSEQ
, "errno is %d", errno
);
62 ret
= wctomb(&mbc
, wcs
[0]);
63 OK(ret
== 1, "ret is %d", ret
);
64 OK(mbc
== 84, "mbc is %d", mbc
);
67 ret
= wcstombs(&mbc
, &dbwcs
[0], 1);
68 OK(ret
== -1, "ret is %d", ret
);
69 OK(mbc
== 84, "mbc is %d", mbc
);
71 ret
= wcstombs(mbs
, wcs
, 0);
72 OK(ret
== 0, "ret is %d", ret
);
74 /* The length for the null character (in any locale) is 0, but if you pass a variable, it will be set to 0 and wctomb returns 1 */
75 ret
= wctomb(NULL
, 0);
76 OK(ret
== 0, "ret is %d", ret
);
78 ret
= wctomb(&mbc
, 0);
79 OK(ret
== 1, "ret is %d", ret
);
80 OK(mbc
== 0, "mbc is %d", mbc
);
82 /* msvcr80.dll changes mbc in the following call back to 0, msvcrt.dll from WinXP SP2 leaves it untouched */
84 ret
= wctomb(&mbc
, dbwcs
[0]);
85 OK(ret
== -1, "ret is %d", ret
);
86 OK(errno
== EILSEQ
, "errno is %d", errno
);
87 OK(mbc
== 84, "mbc is %d", mbc
);
89 /* With a real locale, -1 also becomes a possible return value in case of an invalid character */
91 ret
= wcstombs(NULL
, dbwcs
, 0);
92 OK(ret
== -1, "ret is %d", ret
);
93 OK(errno
== EILSEQ
, "errno is %d", errno
);
95 ret
= wcstombs(NULL
, wcs
, 2);
96 OK(ret
== -1, "ret is %d", ret
);
97 OK(errno
== EILSEQ
, "errno is %d", errno
);
99 /* Test if explicitly setting the locale back to "C" also leads to the same results as above */
102 ret
= wcstombs(NULL
, dbwcs
, 0);
103 OK(ret
== 2, "ret is %d", ret
);
105 ret
= wcstombs(NULL
, wcs
, 0);
106 OK(ret
== 4, "ret is %d", ret
);
108 /* Test wctomb() as well */
109 SETLOCALE("English");
111 ret
= wctomb(&mbc
, wc1
);
112 OK(ret
== 1, "ret is %d", ret
);
113 OK(mbc
== -28, "mbc is %d", mbc
);
115 ret
= wctomb(&mbc
, wc2
);
116 OK(ret
== -1, "ret is %d", ret
);
117 OK(errno
== EILSEQ
, "errno is %d", errno
);
118 OK(mbc
== 63, "mbc is %d", mbc
);
120 SETLOCALE("Russian");
122 ret
= wcstombs(mbs
, wcs
, sizeof(mbs
));
123 OK(ret
== 4, "ret is %d", ret
);
124 OK(!strcmp(mbs
, "Thði"), "mbs is %s", mbs
);
126 ret
= wctomb(&mbc
, wc2
);
127 OK(ret
== 1, "ret is %d", ret
);
128 OK(mbc
== -16, "mbc is %d", mbc
);
130 ret
= wctomb(&mbc
, wc1
);
131 OK(ret
== 1, "ret is %d", ret
);
132 OK(mbc
== 97, "mbc is %d", mbc
);
134 SETLOCALE("English");
136 ret
= wcstombs(&mbc
, wcs
, 1);
137 OK(ret
== 1, "ret is %d", ret
);
138 OK(mbc
== 84, "mbc is %d", mbc
);
140 ZeroMemory(mbs
, sizeof(mbs
));
141 ret
= wcstombs(mbs
, wcs
, sizeof(mbs
));
142 OK(ret
== -1, "ret is %d", ret
);
143 OK(errno
== EILSEQ
, "errno is %d", errno
);
144 OK(!strcmp(mbs
, "Th?i"), "mbs is %s", mbs
);
147 /* wcstombs mustn't add any null character automatically.
148 So in this case, we should get the same string again, even if we only copied the first three bytes. */
149 ret
= wcstombs(mbs
, wcs
, 3);
150 OK(ret
== -1, "ret is %d", ret
);
151 OK(errno
== EILSEQ
, "errno is %d", errno
);
152 OK(!strcmp(mbs
, "Th?i"), "mbs is %s", mbs
);
155 /* Now this shouldn't be the case like above as we zeroed the complete string buffer. */
156 ret
= wcstombs(mbs
, wcs
, 3);
157 OK(ret
== -1, "ret is %d", ret
);
158 OK(errno
== EILSEQ
, "errno is %d", errno
);
159 OK(!strcmp(mbs
, "Th?"), "mbs is %s", mbs
);
161 /* Double-byte tests */
162 SETLOCALE("Chinese");
163 ret
= wcstombs(mbs
, dbwcs
, sizeof(mbs
));
164 OK(ret
== 4, "ret is %d", ret
);
165 OK(!strcmp(mbs
, "µH©Ò"), "mbs is %s", mbs
);
168 /* Length-only tests */
169 SETLOCALE("English");
170 ret
= wcstombs(NULL
, wcs
, 0);
171 OK(ret
== -1, "ret is %d", ret
);
172 OK(errno
== EILSEQ
, "errno is %d", errno
);
174 SETLOCALE("Chinese");
175 ret
= wcstombs(NULL
, dbwcs
, 0);
176 OK(ret
== 4, "ret is %d", ret
);
178 /* This call causes an ERROR_INSUFFICIENT_BUFFER in the called WideCharToMultiByte function.
179 For some reason, wcstombs under Windows doesn't reset the last error to the previous value here, so we can check for ERROR_INSUFFICIENT_BUFFER with GetLastError().
180 This could also be seen as an indication that Windows uses WideCharToMultiByte internally for wcstombs. */
181 ret
= wcstombs(mbs
, dbwcs
, 1);
182 OK(ret
== 0, "ret is %d", ret
);
183 OK(mbs
[0] == 0, "mbs[0] is %d", mbs
[0]);
185 /* ERROR_INSUFFICIENT_BUFFER is also the result of this call with SBCS characters. WTF?!
186 Anyway this is a Win32 error not related to the CRT, so we leave out this criteria. */
187 ret
= wcstombs(mbs
, wcs
, 1);
188 OK(ret
== 1, "ret is %d", ret
);
189 OK(mbs
[0] == 84, "mbs[0] is %d", mbs
[0]);
194 void Win32_Tests(LPBOOL bUsedDefaultChar
)
198 SetLastError(0xdeadbeef);
203 ret
= WideCharToMultiByte(1252, 0, &wc1
, 1, &mbc
, 1, NULL
, bUsedDefaultChar
);
204 OK(ret
== 1, "ret is %d", ret
);
205 OK(mbc
== -28, "mbc is %d", mbc
);
206 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
207 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
209 ret
= WideCharToMultiByte(1252, 0, &wc2
, 1, &mbc
, 1, NULL
, bUsedDefaultChar
);
210 OK(ret
== 1, "ret is %d", ret
);
211 OK(mbc
== 63, "mbc is %d", mbc
);
212 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== TRUE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
213 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
215 ret
= WideCharToMultiByte(1251, 0, &wc2
, 1, &mbc
, 1, NULL
, bUsedDefaultChar
);
216 OK(ret
== 1, "ret is %d", ret
);
217 OK(mbc
== -16, "mbc is %d", mbc
);
218 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
219 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
221 ret
= WideCharToMultiByte(1251, 0, &wc1
, 1, &mbc
, 1, NULL
, bUsedDefaultChar
);
222 OK(ret
== 1, "ret is %d", ret
);
223 OK(mbc
== 97, "mbc is %d", mbc
);
224 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
225 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
227 /* This call triggers the last Win32 error */
228 ret
= WideCharToMultiByte(1252, 0, wcs
, -1, &mbc
, 1, NULL
, bUsedDefaultChar
);
229 OK(ret
== 0, "ret is %d", ret
);
230 OK(mbc
== 84, "mbc is %d", mbc
);
231 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
232 OK(GetLastError() == ERROR_INSUFFICIENT_BUFFER
, "GetLastError() is %lu", GetLastError());
233 SetLastError(0xdeadbeef);
235 ret
= WideCharToMultiByte(1252, 0, wcs
, -1, mbs
, sizeof(mbs
), NULL
, bUsedDefaultChar
);
236 OK(ret
== 5, "ret is %d", ret
);
237 OK(!strcmp(mbs
, "Th?i"), "mbs is %s", mbs
);
238 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== TRUE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
239 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
242 /* WideCharToMultiByte mustn't add any null character automatically.
243 So in this case, we should get the same string again, even if we only copied the first three bytes. */
244 ret
= WideCharToMultiByte(1252, 0, wcs
, 3, mbs
, sizeof(mbs
), NULL
, bUsedDefaultChar
);
245 OK(ret
== 3, "ret is %d", ret
);
246 OK(!strcmp(mbs
, "Th?i"), "mbs is %s", mbs
);
247 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== TRUE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
248 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
251 /* Now this shouldn't be the case like above as we zeroed the complete string buffer. */
252 ret
= WideCharToMultiByte(1252, 0, wcs
, 3, mbs
, sizeof(mbs
), NULL
, bUsedDefaultChar
);
253 OK(ret
== 3, "ret is %d", ret
);
254 OK(!strcmp(mbs
, "Th?"), "mbs is %s", mbs
);
255 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== TRUE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
256 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
258 /* Double-byte tests */
259 ret
= WideCharToMultiByte(950, 0, dbwcs
, -1, mbs
, sizeof(mbs
), NULL
, bUsedDefaultChar
);
260 OK(ret
== 5, "ret is %d", ret
);
261 OK(!strcmp(mbs
, "µH©Ò"), "mbs is %s", mbs
);
262 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
263 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
265 ret
= WideCharToMultiByte(950, 0, dbwcs
, 1, &mbc
, 1, NULL
, bUsedDefaultChar
);
266 OK(ret
== 0, "ret is %d", ret
);
267 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar == FALSE");
268 OK(GetLastError() == ERROR_INSUFFICIENT_BUFFER
, "GetLastError() is %lu", GetLastError());
269 SetLastError(0xdeadbeef);
272 ret
= WideCharToMultiByte(950, 0, dbwcs
, 1, mbs
, sizeof(mbs
), NULL
, bUsedDefaultChar
);
273 OK(ret
== 2, "ret is %d", ret
);
274 OK(!strcmp(mbs
, "µH"), "mbs is %s", mbs
);
275 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
276 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
278 /* Length-only tests */
279 ret
= WideCharToMultiByte(1252, 0, &wc2
, 1, NULL
, 0, NULL
, bUsedDefaultChar
);
280 OK(ret
== 1, "ret is %d", ret
);
281 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== TRUE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
282 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
284 ret
= WideCharToMultiByte(1252, 0, wcs
, -1, NULL
, 0, NULL
, bUsedDefaultChar
);
285 OK(ret
== 5, "ret is %d", ret
);
286 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== TRUE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
287 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
289 ret
= WideCharToMultiByte(950, 0, dbwcs
, 1, NULL
, 0, NULL
, bUsedDefaultChar
);
290 OK(ret
== 2, "ret is %d", ret
);
291 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
292 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
294 ret
= WideCharToMultiByte(950, 0, dbwcs
, -1, NULL
, 0, NULL
, bUsedDefaultChar
);
295 OK(ret
== 5, "ret is %d", ret
);
296 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
297 OK(GetLastError() == 0xdeadbeef, "GetLastError() is %lu", GetLastError());
299 /* Abnormal uses of WideCharToMultiByte */
300 ret
= WideCharToMultiByte(1252, 0, NULL
, 5, mbs
, sizeof(mbs
), NULL
, bUsedDefaultChar
);
301 OK(ret
== 0, "ret is %d", ret
);
302 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== FALSE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
303 OK(GetLastError() == ERROR_INVALID_PARAMETER
, "GetLastError() is %lu", GetLastError());
304 SetLastError(0xdeadbeef);
306 ret
= WideCharToMultiByte(0, 0, dbwcs
, 5, mbs
, sizeof(mbs
), NULL
, bUsedDefaultChar
);
307 OK(ret
== 5, "ret is %d", ret
);
308 OK(!strcmp(mbs
, "??"), "mbs is %s", mbs
);
309 if(bUsedDefaultChar
) OK(*bUsedDefaultChar
== TRUE
, "bUsedDefaultChar is %d", *bUsedDefaultChar
);
311 ret
= WideCharToMultiByte(1252, 0, wcs
, -1, (LPSTR
)wcs
, 5, NULL
, bUsedDefaultChar
);
312 OK(ret
== 0, "ret is %d", ret
);
313 OK(GetLastError() == ERROR_INVALID_PARAMETER
, "GetLastError() is %lu", GetLastError());
314 SetLastError(0xdeadbeef);
316 ret
= WideCharToMultiByte(1252, 0, wcs
, -1, mbs
, -1, NULL
, bUsedDefaultChar
);
317 OK(ret
== 0, "ret is %d", ret
);
318 OK(GetLastError() == ERROR_INVALID_PARAMETER
, "GetLastError() is %lu", GetLastError());
319 SetLastError(0xdeadbeef);
326 BOOL UsedDefaultChar
;
330 /* There are two code pathes in WideCharToMultiByte, one when Flags || DefaultChar || UsedDefaultChar is set and one when it's not.
333 Win32_Tests(&UsedDefaultChar
);