2828bf12d5104ddbce541a6fd2708c00c0551332
[reactos.git] / rostests / tests / wcstombs-tests / wcstombs-tests.c
1 /*
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>
7 */
8
9 #include <windows.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <locale.h>
13 #include <errno.h>
14
15 /* Macros for simplification */
16 #define SETLOCALE(locale) \
17 loc = setlocale(LC_ALL, locale); \
18 if(!loc) \
19 { \
20 puts("setlocale failed for " locale ", this locale is probably not installed on your system"); \
21 return; \
22 }
23
24 #define OK(condition, fail_message, ...) \
25 if(!(condition)) \
26 printf("%d: " fail_message "\n", __LINE__, ##__VA_ARGS__);
27
28 /* Global variables for easier handling */
29 char mbc;
30 char mbs[5];
31 int ret;
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 */
36
37
38 void CRT_Tests()
39 {
40 char* loc;
41
42 puts("CRT-Tests");
43 puts("---------");
44
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);
48
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);
53
54 ret = wcstombs(NULL, wcs, 0);
55 OK(ret == 4, "ret is %d", ret);
56
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);
61
62 ret = wctomb(&mbc, wcs[0]);
63 OK(ret == 1, "ret is %d", ret);
64 OK(mbc == 84, "mbc is %d", mbc);
65
66 mbc = 84;
67 ret = wcstombs(&mbc, &dbwcs[0], 1);
68 OK(ret == -1, "ret is %d", ret);
69 OK(mbc == 84, "mbc is %d", mbc);
70
71 ret = wcstombs(mbs, wcs, 0);
72 OK(ret == 0, "ret is %d", ret);
73
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);
77
78 ret = wctomb(&mbc, 0);
79 OK(ret == 1, "ret is %d", ret);
80 OK(mbc == 0, "mbc is %d", mbc);
81
82 /* msvcr80.dll changes mbc in the following call back to 0, msvcrt.dll from WinXP SP2 leaves it untouched */
83 mbc = 84;
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);
88
89 /* With a real locale, -1 also becomes a possible return value in case of an invalid character */
90 SETLOCALE("German");
91 ret = wcstombs(NULL, dbwcs, 0);
92 OK(ret == -1, "ret is %d", ret);
93 OK(errno == EILSEQ, "errno is %d", errno);
94
95 ret = wcstombs(NULL, wcs, 2);
96 OK(ret == -1, "ret is %d", ret);
97 OK(errno == EILSEQ, "errno is %d", errno);
98
99 /* Test if explicitly setting the locale back to "C" also leads to the same results as above */
100 SETLOCALE("C");
101
102 ret = wcstombs(NULL, dbwcs, 0);
103 OK(ret == 2, "ret is %d", ret);
104
105 ret = wcstombs(NULL, wcs, 0);
106 OK(ret == 4, "ret is %d", ret);
107
108 /* Test wctomb() as well */
109 SETLOCALE("English");
110
111 ret = wctomb(&mbc, wc1);
112 OK(ret == 1, "ret is %d", ret);
113 OK(mbc == -28, "mbc is %d", mbc);
114
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);
119
120 SETLOCALE("Russian");
121
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);
125
126 ret = wctomb(&mbc, wc2);
127 OK(ret == 1, "ret is %d", ret);
128 OK(mbc == -16, "mbc is %d", mbc);
129
130 ret = wctomb(&mbc, wc1);
131 OK(ret == 1, "ret is %d", ret);
132 OK(mbc == 97, "mbc is %d", mbc);
133
134 SETLOCALE("English");
135
136 ret = wcstombs(&mbc, wcs, 1);
137 OK(ret == 1, "ret is %d", ret);
138 OK(mbc == 84, "mbc is %d", mbc);
139
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);
145 mbs[0] = 0;
146
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);
153 ZeroMemory(mbs, 5);
154
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);
160
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);
166 ZeroMemory(mbs, 5);
167
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);
173
174 SETLOCALE("Chinese");
175 ret = wcstombs(NULL, dbwcs, 0);
176 OK(ret == 4, "ret is %d", ret);
177
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]);
184
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]);
190
191 putchar('\n');
192 }
193
194 void Win32_Tests(LPBOOL bUsedDefaultChar)
195 {
196 /*int i;*/
197
198 SetLastError(0xdeadbeef);
199
200 puts("Win32-Tests");
201 puts("-----------");
202
203 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, &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());
208
209 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, &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());
214
215 ret = WideCharToMultiByte(1251, WC_NO_BEST_FIT_CHARS, &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());
220
221 ret = WideCharToMultiByte(1251, WC_NO_BEST_FIT_CHARS, &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());
226
227 /* This call triggers the last Win32 error */
228 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, 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);
234
235 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, 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());
240 mbs[0] = 0;
241
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, WC_NO_BEST_FIT_CHARS, 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());
249 ZeroMemory(mbs, 5);
250
251 /* Now this shouldn't be the case like above as we zeroed the complete string buffer. */
252 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, 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());
257
258 /* Double-byte tests */
259 ret = WideCharToMultiByte(950, WC_NO_BEST_FIT_CHARS, 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());
264
265 ret = WideCharToMultiByte(950, WC_NO_BEST_FIT_CHARS, 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);
270 ZeroMemory(mbs, 5);
271
272 ret = WideCharToMultiByte(950, WC_NO_BEST_FIT_CHARS, 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());
277
278 /* Length-only tests */
279 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, &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());
283
284 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, 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());
288
289 ret = WideCharToMultiByte(950, WC_NO_BEST_FIT_CHARS, 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());
293
294 ret = WideCharToMultiByte(950, WC_NO_BEST_FIT_CHARS, 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());
298
299 /* Abnormal uses of WideCharToMultiByte */
300 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, 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);
305
306 ret = WideCharToMultiByte(0, WC_NO_BEST_FIT_CHARS, 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);
310
311 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, 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);
315
316 ret = WideCharToMultiByte(1252, WC_NO_BEST_FIT_CHARS, 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);
320
321 putchar('\n');
322 }
323
324 int main()
325 {
326 BOOL UsedDefaultChar;
327
328 CRT_Tests();
329
330 /* There are two code pathes in WideCharToMultiByte, one when Flags || DefaultChar || UsedDefaultChar is set and one when it's not.
331 Test both here. */
332 Win32_Tests(NULL);
333 Win32_Tests(&UsedDefaultChar);
334
335 return 0;
336 }