winex11.drv: Map coordinates before calling send_mouse_input.
[wine/zf.git] / dlls / kernel32 / tests / codepage.c
blob42e49888e053309680f2bd0190c95d00f8411fc1
1 /*
2 * Unit tests for code page to/from unicode translations
4 * Copyright (c) 2002 Dmitry Timoshkov
5 * Copyright (c) 2008 Colin Finck
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <limits.h>
26 #include "wine/test.h"
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
31 static const char foobarA[] = "foobar";
32 static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
34 static void test_destination_buffer(void)
36 LPSTR buffer;
37 INT maxsize;
38 INT needed;
39 INT len;
41 SetLastError(0xdeadbeef);
42 needed = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, NULL, 0, NULL, NULL);
43 ok( (needed > 0), "returned %d with %u (expected '> 0')\n",
44 needed, GetLastError());
46 maxsize = needed*2;
47 buffer = HeapAlloc(GetProcessHeap(), 0, maxsize);
48 if (buffer == NULL) return;
50 maxsize--;
51 memset(buffer, 'x', maxsize);
52 buffer[maxsize] = '\0';
53 SetLastError(0xdeadbeef);
54 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, needed+1, NULL, NULL);
55 ok( (len > 0), "returned %d with %u and '%s' (expected '> 0')\n",
56 len, GetLastError(), buffer);
58 memset(buffer, 'x', maxsize);
59 buffer[maxsize] = '\0';
60 SetLastError(0xdeadbeef);
61 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, needed, NULL, NULL);
62 ok( (len > 0), "returned %d with %u and '%s' (expected '> 0')\n",
63 len, GetLastError(), buffer);
65 memset(buffer, 'x', maxsize);
66 buffer[maxsize] = '\0';
67 SetLastError(0xdeadbeef);
68 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, needed-1, NULL, NULL);
69 ok( !len && (GetLastError() == ERROR_INSUFFICIENT_BUFFER),
70 "returned %d with %u and '%s' (expected '0' with "
71 "ERROR_INSUFFICIENT_BUFFER)\n", len, GetLastError(), buffer);
73 memset(buffer, 'x', maxsize);
74 buffer[maxsize] = '\0';
75 SetLastError(0xdeadbeef);
76 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, 1, NULL, NULL);
77 ok( !len && (GetLastError() == ERROR_INSUFFICIENT_BUFFER),
78 "returned %d with %u and '%s' (expected '0' with "
79 "ERROR_INSUFFICIENT_BUFFER)\n", len, GetLastError(), buffer);
81 SetLastError(0xdeadbeef);
82 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, buffer, 0, NULL, NULL);
83 ok( (len > 0), "returned %d with %u (expected '> 0')\n",
84 len, GetLastError());
86 SetLastError(0xdeadbeef);
87 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, NULL, needed, NULL, NULL);
88 ok( !len && (GetLastError() == ERROR_INVALID_PARAMETER),
89 "returned %d with %u (expected '0' with "
90 "ERROR_INVALID_PARAMETER)\n", len, GetLastError());
92 HeapFree(GetProcessHeap(), 0, buffer);
96 static void test_null_source(void)
98 int len;
99 DWORD GLE;
101 SetLastError(0);
102 len = WideCharToMultiByte(CP_ACP, 0, NULL, 0, NULL, 0, NULL, NULL);
103 GLE = GetLastError();
104 ok(!len && GLE == ERROR_INVALID_PARAMETER,
105 "WideCharToMultiByte returned %d with GLE=%u (expected 0 with ERROR_INVALID_PARAMETER)\n",
106 len, GLE);
108 SetLastError(0);
109 len = WideCharToMultiByte(CP_ACP, 0, NULL, -1, NULL, 0, NULL, NULL);
110 GLE = GetLastError();
111 ok(!len && GLE == ERROR_INVALID_PARAMETER,
112 "WideCharToMultiByte returned %d with GLE=%u (expected 0 with ERROR_INVALID_PARAMETER)\n",
113 len, GLE);
116 static void test_negative_source_length(void)
118 int len;
119 char buf[10];
120 WCHAR bufW[10];
122 /* Test, whether any negative source length works as strlen() + 1 */
123 SetLastError( 0xdeadbeef );
124 memset(buf,'x',sizeof(buf));
125 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -2002, buf, 10, NULL, NULL);
126 ok(len == 7 && GetLastError() == 0xdeadbeef,
127 "WideCharToMultiByte(-2002): len=%d error=%u\n", len, GetLastError());
128 ok(!lstrcmpA(buf, "foobar"),
129 "WideCharToMultiByte(-2002): expected \"foobar\" got \"%s\"\n", buf);
131 SetLastError( 0xdeadbeef );
132 memset(bufW,'x',sizeof(bufW));
133 len = MultiByteToWideChar(CP_ACP, 0, "foobar", -2002, bufW, 10);
134 ok(len == 7 && !lstrcmpW(bufW, foobarW) && GetLastError() == 0xdeadbeef,
135 "MultiByteToWideChar(-2002): len=%d error=%u\n", len, GetLastError());
137 SetLastError(0xdeadbeef);
138 memset(bufW, 'x', sizeof(bufW));
139 len = MultiByteToWideChar(CP_ACP, 0, "foobar", -1, bufW, 6);
140 ok(len == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
141 "MultiByteToWideChar(-1): len=%d error=%u\n", len, GetLastError());
144 #define LONGBUFLEN 100000
145 static void test_negative_dest_length(void)
147 int len, i;
148 static WCHAR bufW[LONGBUFLEN];
149 static char bufA[LONGBUFLEN];
150 static WCHAR originalW[LONGBUFLEN];
151 static char originalA[LONGBUFLEN];
152 DWORD theError;
154 /* Test return on -1 dest length */
155 SetLastError( 0xdeadbeef );
156 memset(bufA,'x',sizeof(bufA));
157 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, -1, NULL, NULL);
158 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
159 "WideCharToMultiByte(destlen -1): len=%d error=%x\n", len, GetLastError());
161 SetLastError( 0xdeadbeef );
162 memset(bufW,'x',sizeof(bufW));
163 len = MultiByteToWideChar(CP_ACP, 0, foobarA, -1, bufW, -1);
164 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
165 "MultiByteToWideChar(destlen -1): len=%d error=%x\n", len, GetLastError());
167 /* Test return on -1000 dest length */
168 SetLastError( 0xdeadbeef );
169 memset(bufA,'x',sizeof(bufA));
170 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, -1000, NULL, NULL);
171 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
172 "WideCharToMultiByte(destlen -1000): len=%d error=%x\n", len, GetLastError());
174 SetLastError( 0xdeadbeef );
175 memset(bufW,'x',sizeof(bufW));
176 len = MultiByteToWideChar(CP_ACP, 0, foobarA, -1000, bufW, -1);
177 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
178 "MultiByteToWideChar(destlen -1000): len=%d error=%x\n", len, GetLastError());
180 /* Test return on INT_MAX dest length */
181 SetLastError( 0xdeadbeef );
182 memset(bufA,'x',sizeof(bufA));
183 len = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, bufA, INT_MAX, NULL, NULL);
184 ok(len == 7 && !lstrcmpA(bufA, "foobar") && GetLastError() == 0xdeadbeef,
185 "WideCharToMultiByte(destlen INT_MAX): len=%d error=%x\n", len, GetLastError());
187 /* Test return on INT_MAX dest length and very long input */
188 SetLastError( 0xdeadbeef );
189 memset(bufA,'x',sizeof(bufA));
190 for (i=0; i < LONGBUFLEN - 1; i++) {
191 originalW[i] = 'Q';
192 originalA[i] = 'Q';
194 originalW[LONGBUFLEN-1] = 0;
195 originalA[LONGBUFLEN-1] = 0;
196 len = WideCharToMultiByte(CP_ACP, 0, originalW, -1, bufA, INT_MAX, NULL, NULL);
197 theError = GetLastError();
198 ok(len == LONGBUFLEN && !lstrcmpA(bufA, originalA) && theError == 0xdeadbeef,
199 "WideCharToMultiByte(srclen %d, destlen INT_MAX): len %d error=%x\n", LONGBUFLEN, len, theError);
203 static void test_other_invalid_parameters(void)
205 char c_string[] = "Hello World";
206 size_t c_string_len = sizeof(c_string);
207 WCHAR w_string[] = {'H','e','l','l','o',' ','W','o','r','l','d',0};
208 size_t w_string_len = ARRAY_SIZE(w_string);
209 BOOL used;
210 INT len;
212 /* Unrecognized flag => ERROR_INVALID_FLAGS */
213 SetLastError(0xdeadbeef);
214 len = WideCharToMultiByte(CP_ACP, 0x100, w_string, -1, c_string, c_string_len, NULL, NULL);
215 ok(len == 0 && GetLastError() == ERROR_INVALID_FLAGS, "len=%d error=%x\n", len, GetLastError());
217 SetLastError(0xdeadbeef);
218 len = WideCharToMultiByte(CP_ACP, 0x800, w_string, -1, c_string, c_string_len, NULL, NULL);
219 ok(len == 0 && GetLastError() == ERROR_INVALID_FLAGS, "len=%d error=%x\n", len, GetLastError());
221 SetLastError(0xdeadbeef);
222 len = MultiByteToWideChar(CP_ACP, 0x10, c_string, -1, w_string, w_string_len);
223 ok(len == 0 && GetLastError() == ERROR_INVALID_FLAGS, "len=%d error=%x\n", len, GetLastError());
226 /* Unrecognized flag and invalid codepage => ERROR_INVALID_PARAMETER */
227 SetLastError(0xdeadbeef);
228 len = WideCharToMultiByte(0xdeadbeef, 0x100, w_string, w_string_len, c_string, c_string_len, NULL, NULL);
229 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
231 SetLastError(0xdeadbeef);
232 len = MultiByteToWideChar(0xdeadbeef, 0x10, c_string, c_string_len, w_string, w_string_len);
233 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
236 /* Unrecognized flag and src is NULL => ERROR_INVALID_PARAMETER */
237 SetLastError(0xdeadbeef);
238 len = WideCharToMultiByte(CP_ACP, 0x100, NULL, -1, c_string, c_string_len, NULL, NULL);
239 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
241 SetLastError(0xdeadbeef);
242 len = MultiByteToWideChar(CP_ACP, 0x10, NULL, -1, w_string, w_string_len);
243 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
246 /* srclen=0 => ERROR_INVALID_PARAMETER */
247 SetLastError(0xdeadbeef);
248 len = WideCharToMultiByte(CP_ACP, 0, w_string, 0, c_string, c_string_len, NULL, NULL);
249 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
251 SetLastError(0xdeadbeef);
252 len = MultiByteToWideChar(CP_ACP, 0, c_string, 0, w_string, w_string_len);
253 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
256 /* dst=NULL but dstlen not 0 => ERROR_INVALID_PARAMETER */
257 SetLastError(0xdeadbeef);
258 len = WideCharToMultiByte(CP_ACP, 0, w_string, w_string_len, NULL, c_string_len, NULL, NULL);
259 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
261 SetLastError(0xdeadbeef);
262 len = MultiByteToWideChar(CP_ACP, 0, c_string, c_string_len, NULL, w_string_len);
263 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
266 /* CP_UTF7, CP_UTF8, or CP_SYMBOL and defchar not NULL => ERROR_INVALID_PARAMETER */
267 /* CP_SYMBOL's behavior here is undocumented */
268 SetLastError(0xdeadbeef);
269 len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL);
270 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
272 SetLastError(0xdeadbeef);
273 len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL);
274 ok((len == 0 && GetLastError() == ERROR_INVALID_PARAMETER)
275 || broken(len == 12) /* Win10 1709+ */, "len=%d error=%x\n", len, GetLastError());
277 SetLastError(0xdeadbeef);
278 len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, c_string, NULL);
279 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
282 /* CP_UTF7, CP_UTF8, or CP_SYMBOL and used not NULL => ERROR_INVALID_PARAMETER */
283 /* CP_SYMBOL's behavior here is undocumented */
284 SetLastError(0xdeadbeef);
285 len = WideCharToMultiByte(CP_UTF7, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used);
286 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
288 SetLastError(0xdeadbeef);
289 len = WideCharToMultiByte(CP_UTF8, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used);
290 ok((len == 0 && GetLastError() == ERROR_INVALID_PARAMETER)
291 || broken(len == 12) /* Win10 1709+ */, "len=%d error=%x\n", len, GetLastError());
293 SetLastError(0xdeadbeef);
294 len = WideCharToMultiByte(CP_SYMBOL, 0, w_string, w_string_len, c_string, c_string_len, NULL, &used);
295 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
298 /* CP_UTF7, flags not 0 and used not NULL => ERROR_INVALID_PARAMETER */
299 /* (tests precedence of ERROR_INVALID_PARAMETER over ERROR_INVALID_FLAGS) */
300 /* The same test with CP_SYMBOL instead of CP_UTF7 gives ERROR_INVALID_FLAGS
301 instead except on Windows NT4 */
302 SetLastError(0xdeadbeef);
303 len = WideCharToMultiByte(CP_UTF7, 1, w_string, w_string_len, c_string, c_string_len, NULL, &used);
304 ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "len=%d error=%x\n", len, GetLastError());
306 /* CP_UTF8, unrecognized flag and used not NULL => ERROR_INVALID_PARAMETER */
307 SetLastError(0xdeadbeef);
308 len = WideCharToMultiByte(CP_UTF8, 0x100, w_string, w_string_len, c_string, c_string_len, NULL, &used);
309 ok(len == 0, "wrong ret %d\n", len);
310 ok(GetLastError() == ERROR_INVALID_PARAMETER
311 || GetLastError() == ERROR_INVALID_FLAGS /* Win10 1709+ */, "wrong error %u\n", GetLastError());
314 static void test_overlapped_buffers(void)
316 static const WCHAR strW[] = {'j','u','s','t',' ','a',' ','t','e','s','t',0};
317 static const char strA[] = "just a test";
318 char buf[256];
319 int ret;
321 /* limit such that strA's NUL terminator overlaps strW's NUL */
322 size_t overlap_limit = (sizeof(strW)-sizeof(strA)) - (sizeof(strW[0])-sizeof(strA[0]));
324 SetLastError(0xdeadbeef);
325 memcpy(buf + 1, strW, sizeof(strW));
326 ret = WideCharToMultiByte(CP_ACP, 0, (WCHAR *)(buf + 1), -1, buf, sizeof(buf), NULL, NULL);
327 ok(ret == sizeof(strA), "unexpected ret %d\n", ret);
328 ok(!memcmp(buf, strA, sizeof(strA)), "conversion failed: %s\n", buf);
329 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
331 SetLastError(0xdeadbeef);
332 memcpy(buf + overlap_limit, strA, sizeof(strA));
333 ret = MultiByteToWideChar(CP_ACP, 0, buf + overlap_limit, -1, (WCHAR *)buf, sizeof(buf) / sizeof(WCHAR));
334 ok(ret == ARRAY_SIZE(strW), "unexpected ret %d\n", ret);
335 ok(!memcmp(buf, strW, sizeof(strW)), "conversion failed: %s\n", wine_dbgstr_wn((WCHAR *)buf, ARRAY_SIZE(strW)));
336 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
339 static void test_string_conversion(LPBOOL bUsedDefaultChar)
341 char mbc;
342 char mbs[15];
343 int ret;
344 WCHAR wc1 = 228; /* Western Windows-1252 character */
345 WCHAR wc2 = 1088; /* Russian Windows-1251 character not displayable for Windows-1252 */
346 static const WCHAR wcs[] = {'T', 'h', 1088, 'i', 0}; /* String with ASCII characters and a Russian character */
347 static const WCHAR dbwcs[] = {28953, 25152, 0}; /* String with Chinese (codepage 950) characters */
348 static const WCHAR dbwcs2[] = {0x7bb8, 0x3d, 0xc813, 0xac00, 0xb77d, 0};
349 static const char default_char[] = {0xa3, 0xbf, 0};
351 SetLastError(0xdeadbeef);
352 ret = WideCharToMultiByte(1252, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
353 ok(ret == 1, "ret is %d\n", ret);
354 ok(mbc == '\xe4', "mbc is %d\n", mbc);
355 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
356 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
358 SetLastError(0xdeadbeef);
359 ret = WideCharToMultiByte(1252, 0, &wc2, 1, &mbc, 1, NULL, bUsedDefaultChar);
360 ok(ret == 1, "ret is %d\n", ret);
361 ok(mbc == 63, "mbc is %d\n", mbc);
362 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
363 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
365 if (IsValidCodePage(1251))
367 SetLastError(0xdeadbeef);
368 ret = WideCharToMultiByte(1251, 0, &wc2, 1, &mbc, 1, NULL, bUsedDefaultChar);
369 ok(ret == 1, "ret is %d\n", ret);
370 ok(mbc == '\xf0', "mbc is %d\n", mbc);
371 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
372 ok(GetLastError() == 0xdeadbeef ||
373 broken(GetLastError() == 0), /* win95 */
374 "GetLastError() is %u\n", GetLastError());
376 SetLastError(0xdeadbeef);
377 ret = WideCharToMultiByte(1251, 0, &wc1, 1, &mbc, 1, NULL, bUsedDefaultChar);
378 ok(ret == 1, "ret is %d\n", ret);
379 ok(mbc == 97, "mbc is %d\n", mbc);
380 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
381 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
383 else
384 skip("Codepage 1251 not available\n");
386 /* This call triggers the last Win32 error */
387 SetLastError(0xdeadbeef);
388 ret = WideCharToMultiByte(1252, 0, wcs, -1, &mbc, 1, NULL, bUsedDefaultChar);
389 ok(ret == 0, "ret is %d\n", ret);
390 ok(mbc == 84, "mbc is %d\n", mbc);
391 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
392 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %u\n", GetLastError());
394 SetLastError(0xdeadbeef);
395 ret = WideCharToMultiByte(1252, 0, wcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
396 ok(ret == 5, "ret is %d\n", ret);
397 ok(!strcmp(mbs, "Th?i"), "mbs is %s\n", mbs);
398 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
399 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
400 mbs[0] = 0;
402 /* WideCharToMultiByte mustn't add any null character automatically.
403 So in this case, we should get the same string again, even if we only copied the first three bytes. */
404 SetLastError(0xdeadbeef);
405 ret = WideCharToMultiByte(1252, 0, wcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
406 ok(ret == 3, "ret is %d\n", ret);
407 ok(!strcmp(mbs, "Th?i"), "mbs is %s\n", mbs);
408 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
409 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
410 ZeroMemory(mbs, 5);
412 /* Now this shouldn't be the case like above as we zeroed the complete string buffer. */
413 SetLastError(0xdeadbeef);
414 ret = WideCharToMultiByte(1252, 0, wcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
415 ok(ret == 3, "ret is %d\n", ret);
416 ok(!strcmp(mbs, "Th?"), "mbs is %s\n", mbs);
417 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
418 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
420 /* Double-byte tests */
421 ret = WideCharToMultiByte(1252, 0, dbwcs, 3, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
422 ok(ret == 3, "ret is %d\n", ret);
423 ok(!strcmp(mbs, "??"), "mbs is %s\n", mbs);
424 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
426 ret = WideCharToMultiByte(936, WC_COMPOSITECHECK, dbwcs2, -1, mbs, sizeof(mbs), (const char *)default_char, bUsedDefaultChar);
427 ok(ret == 10, "ret is %d\n", ret);
428 ok(!strcmp(mbs, "\xf3\xe7\x3d\xa3\xbf\xa3\xbf\xa3\xbf"), "mbs is %s\n", mbs);
429 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
431 /* Length-only tests */
432 SetLastError(0xdeadbeef);
433 ret = WideCharToMultiByte(1252, 0, &wc2, 1, NULL, 0, NULL, bUsedDefaultChar);
434 ok(ret == 1, "ret is %d\n", ret);
435 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
436 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
438 SetLastError(0xdeadbeef);
439 ret = WideCharToMultiByte(1252, 0, wcs, -1, NULL, 0, NULL, bUsedDefaultChar);
440 ok(ret == 5, "ret is %d\n", ret);
441 if(bUsedDefaultChar) ok(*bUsedDefaultChar == TRUE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
442 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
444 if (!IsValidCodePage(950))
446 skip("Codepage 950 not available\n");
447 return;
450 /* Double-byte tests */
451 SetLastError(0xdeadbeef);
452 ret = WideCharToMultiByte(950, 0, dbwcs, -1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
453 ok(ret == 5, "ret is %d\n", ret);
454 ok(!strcmp(mbs, "\xb5H\xa9\xd2"), "mbs is %s\n", mbs);
455 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
456 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
458 SetLastError(0xdeadbeef);
459 ret = WideCharToMultiByte(950, 0, dbwcs, 1, &mbc, 1, NULL, bUsedDefaultChar);
460 ok(ret == 0, "ret is %d\n", ret);
461 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
462 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "GetLastError() is %u\n", GetLastError());
463 ZeroMemory(mbs, 5);
465 SetLastError(0xdeadbeef);
466 ret = WideCharToMultiByte(950, 0, dbwcs, 1, mbs, sizeof(mbs), NULL, bUsedDefaultChar);
467 ok(ret == 2, "ret is %d\n", ret);
468 ok(!strcmp(mbs, "\xb5H"), "mbs is %s\n", mbs);
469 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
470 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
472 /* Length-only tests */
473 SetLastError(0xdeadbeef);
474 ret = WideCharToMultiByte(950, 0, dbwcs, 1, NULL, 0, NULL, bUsedDefaultChar);
475 ok(ret == 2, "ret is %d\n", ret);
476 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
477 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
479 SetLastError(0xdeadbeef);
480 ret = WideCharToMultiByte(950, 0, dbwcs, -1, NULL, 0, NULL, bUsedDefaultChar);
481 ok(ret == 5, "ret is %d\n", ret);
482 if(bUsedDefaultChar) ok(*bUsedDefaultChar == FALSE, "bUsedDefaultChar is %d\n", *bUsedDefaultChar);
483 ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
486 static void test_utf7_encoding(void)
488 WCHAR input[16];
489 char output[16], expected[16];
490 int i, len, expected_len;
492 static const BOOL directly_encodable_table[] =
494 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
495 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
496 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
497 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
498 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
499 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
500 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
501 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 /* 0x70 - 0x7F */
503 static const char base64_encoding_table[] =
504 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
506 const struct
508 /* inputs */
509 WCHAR src[16];
510 int srclen;
511 char *dst;
512 int dstlen;
513 /* expected outputs */
514 char expected_dst[16];
515 int chars_written;
516 int len;
518 tests[] =
520 /* tests string conversion with srclen=-1 */
522 {0x4F60,0x597D,0x5417,0}, -1, output, sizeof(output) - 1,
523 "+T2BZfVQX-", 11, 11
525 /* tests string conversion with srclen=-2 */
527 {0x4F60,0x597D,0x5417,0}, -2, output, sizeof(output) - 1,
528 "+T2BZfVQX-", 11, 11
530 /* tests string conversion with dstlen=strlen(expected_dst) */
532 {0x4F60,0x597D,0x5417,0}, -1, output, 10,
533 "+T2BZfVQX-", 10, 0
535 /* tests string conversion with dstlen=strlen(expected_dst)+1 */
537 {0x4F60,0x597D,0x5417,0}, -1, output, 11,
538 "+T2BZfVQX-", 11, 11
540 /* tests string conversion with dstlen=strlen(expected_dst)+2 */
542 {0x4F60,0x597D,0x5417,0}, -1, output, 12,
543 "+T2BZfVQX-", 11, 11
545 /* tests dry run with dst=NULL and dstlen=0 */
547 {0x4F60,0x597D,0x5417,0}, -1, NULL, 0,
548 {}, 0, 11
550 /* tests dry run with dst!=NULL and dstlen=0 */
552 {0x4F60,0x597D,0x5417,0}, -1, output, 0,
553 {}, 0, 11
555 /* tests srclen < strlenW(src) with directly encodable chars */
557 {'h','e','l','l','o',0}, 2, output, sizeof(output) - 1,
558 "he", 2, 2
560 /* tests srclen < strlenW(src) with non-directly encodable chars */
562 {0x4F60,0x597D,0x5417,0}, 2, output, sizeof(output) - 1,
563 "+T2BZfQ-", 8, 8
565 /* tests a single null char */
567 {0}, -1, output, sizeof(output) - 1,
568 "", 1, 1
570 /* tests a buffer that runs out while not encoding a UTF-7 sequence */
572 {'h','e','l','l','o',0}, -1, output, 2,
573 "he", 2, 0
575 /* tests a buffer that runs out after writing 1 base64 character */
577 {0x4F60,0x0001,0}, -1, output, 2,
578 "+T", 2, 0
580 /* tests a buffer that runs out after writing 2 base64 characters */
582 {0x4F60,0x0001,0}, -1, output, 3,
583 "+T2", 3, 0
585 /* tests a buffer that runs out after writing 3 base64 characters */
587 {0x4F60,0x0001,0}, -1, output, 4,
588 "+T2A", 4, 0
590 /* tests a buffer that runs out just after writing the + sign */
592 {0x4F60,0}, -1, output, 1,
593 "+", 1, 0
595 /* tests a buffer that runs out just before writing the - sign
596 * the number of bits to encode here is evenly divisible by 6 */
598 {0x4F60,0x597D,0x5417,0}, -1, output, 9,
599 "+T2BZfVQX", 9, 0
601 /* tests a buffer that runs out just before writing the - sign
602 * the number of bits to encode here is NOT evenly divisible by 6 */
604 {0x4F60,0}, -1, output, 4,
605 "+T2", 3, 0
607 /* tests a buffer that runs out in the middle of escaping a + sign */
609 {'+',0}, -1, output, 1,
610 "+", 1, 0
614 /* test which characters are encoded if surrounded by non-encoded characters */
615 for (i = 0; i <= 0xFFFF; i++)
617 input[0] = ' ';
618 input[1] = i;
619 input[2] = ' ';
620 input[3] = 0;
622 memset(output, '#', sizeof(output) - 1);
623 output[sizeof(output) - 1] = 0;
625 len = WideCharToMultiByte(CP_UTF7, 0, input, 4, output, sizeof(output) - 1, NULL, NULL);
627 if (i == '+')
629 /* '+' is a special case and is encoded as "+-" */
630 expected_len = 5;
631 strcpy(expected, " +- ");
633 else if (i <= 0x7F && directly_encodable_table[i])
635 /* encodes directly */
636 expected_len = 4;
637 sprintf(expected, " %c ", i);
639 else
641 /* base64-encodes */
642 expected_len = 8;
643 sprintf(expected, " +%c%c%c- ",
644 base64_encoding_table[(i & 0xFC00) >> 10],
645 base64_encoding_table[(i & 0x03F0) >> 4],
646 base64_encoding_table[(i & 0x000F) << 2]);
649 ok(len == expected_len, "i=0x%04x: expected len=%i, got len=%i\n", i, expected_len, len);
650 ok(memcmp(output, expected, expected_len) == 0,
651 "i=0x%04x: expected output='%s', got output='%s'\n", i, expected, output);
652 ok(output[expected_len] == '#', "i=0x%04x: expected output[%i]='#', got output[%i]=%i\n",
653 i, expected_len, expected_len, output[expected_len]);
656 /* test which one-byte characters are absorbed into surrounding base64 blocks
657 * (Windows always ends the base64 block when it encounters a directly encodable character) */
658 for (i = 0; i <= 0xFFFF; i++)
660 input[0] = 0x2672;
661 input[1] = i;
662 input[2] = 0x2672;
663 input[3] = 0;
665 memset(output, '#', sizeof(output) - 1);
666 output[sizeof(output) - 1] = 0;
668 len = WideCharToMultiByte(CP_UTF7, 0, input, 4, output, sizeof(output) - 1, NULL, NULL);
670 if (i == '+')
672 /* '+' is a special case and is encoded as "+-" */
673 expected_len = 13;
674 strcpy(expected, "+JnI-+-+JnI-");
676 else if (i <= 0x7F && directly_encodable_table[i])
678 /* encodes directly */
679 expected_len = 12;
680 sprintf(expected, "+JnI-%c+JnI-", i);
682 else
684 /* base64-encodes */
685 expected_len = 11;
686 sprintf(expected, "+Jn%c%c%c%cZy-",
687 base64_encoding_table[8 | ((i & 0xC000) >> 14)],
688 base64_encoding_table[(i & 0x3F00) >> 8],
689 base64_encoding_table[(i & 0x00FC) >> 2],
690 base64_encoding_table[((i & 0x0003) << 4) | 2]);
693 ok(len == expected_len, "i=0x%04x: expected len=%i, got len=%i\n", i, expected_len, len);
694 ok(memcmp(output, expected, expected_len) == 0,
695 "i=0x%04x: expected output='%s', got output='%s'\n", i, expected, output);
696 ok(output[expected_len] == '#', "i=0x%04x: expected output[%i]='#', got output[%i]=%i\n",
697 i, expected_len, expected_len, output[expected_len]);
700 for (i = 0; i < ARRAY_SIZE(tests); i++)
702 memset(output, '#', sizeof(output) - 1);
703 output[sizeof(output) - 1] = 0;
704 SetLastError(0xdeadbeef);
706 len = WideCharToMultiByte(CP_UTF7, 0, tests[i].src, tests[i].srclen,
707 tests[i].dst, tests[i].dstlen, NULL, NULL);
709 if (!tests[i].len)
711 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
712 "tests[%i]: expected error=0x%x, got error=0x%x\n",
713 i, ERROR_INSUFFICIENT_BUFFER, GetLastError());
715 ok(len == tests[i].len, "tests[%i]: expected len=%i, got len=%i\n", i, tests[i].len, len);
717 if (tests[i].dst)
719 ok(memcmp(tests[i].dst, tests[i].expected_dst, tests[i].chars_written) == 0,
720 "tests[%i]: expected dst='%s', got dst='%s'\n",
721 i, tests[i].expected_dst, tests[i].dst);
722 ok(tests[i].dst[tests[i].chars_written] == '#',
723 "tests[%i]: expected dst[%i]='#', got dst[%i]=%i\n",
724 i, tests[i].chars_written, tests[i].chars_written, tests[i].dst[tests[i].chars_written]);
729 static void test_utf7_decoding(void)
731 char input[32];
732 WCHAR output[32], expected[32];
733 int i, len, expected_len;
735 static const signed char base64_decoding_table[] =
737 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
738 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
739 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
740 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
741 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
742 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
743 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
744 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 /* 0x70-0x7F */
747 struct
749 /* inputs */
750 char src[32];
751 int srclen;
752 WCHAR *dst;
753 int dstlen;
754 /* expected outputs */
755 WCHAR expected_dst[32];
756 int chars_written;
757 int len;
759 tests[] =
761 /* tests string conversion with srclen=-1 */
763 "+T2BZfQ-", -1, output, ARRAY_SIZE(output) - 1,
764 {0x4F60,0x597D,0}, 3, 3
766 /* tests string conversion with srclen=-2 */
768 "+T2BZfQ-", -2, output, ARRAY_SIZE(output) - 1,
769 {0x4F60,0x597D,0}, 3, 3
771 /* tests string conversion with dstlen=strlen(expected_dst) */
773 "+T2BZfQ-", -1, output, 2,
774 {0x4F60,0x597D}, 2, 0
776 /* tests string conversion with dstlen=strlen(expected_dst)+1 */
778 "+T2BZfQ-", -1, output, 3,
779 {0x4F60,0x597D,0}, 3, 3
781 /* tests string conversion with dstlen=strlen(expected_dst)+2 */
783 "+T2BZfQ-", -1, output, 4,
784 {0x4F60,0x597D,0}, 3, 3
786 /* tests dry run with dst=NULL and dstlen=0 */
788 "+T2BZfQ-", -1, NULL, 0,
789 {}, 0, 3
791 /* tests dry run with dst!=NULL and dstlen=0 */
793 "+T2BZfQ-", -1, output, 0,
794 {}, 0, 3
796 /* tests ill-formed UTF-7: 6 bits, not enough for a byte pair */
798 "+T-+T-+T-hello", -1, output, ARRAY_SIZE(output) - 1,
799 {'h','e','l','l','o',0}, 6, 6
801 /* tests ill-formed UTF-7: 12 bits, not enough for a byte pair */
803 "+T2-+T2-+T2-hello", -1, output, ARRAY_SIZE(output) - 1,
804 {'h','e','l','l','o',0}, 6, 6
806 /* tests ill-formed UTF-7: 18 bits, not a multiple of 16 and the last bit is a 1 */
808 "+T2B-+T2B-+T2B-hello", -1, output, ARRAY_SIZE(output) - 1,
809 {0x4F60,0x4F60,0x4F60,'h','e','l','l','o',0}, 9, 9
811 /* tests ill-formed UTF-7: 24 bits, a multiple of 8 but not a multiple of 16 */
813 "+T2BZ-+T2BZ-+T2BZ-hello", -1, output, ARRAY_SIZE(output) - 1,
814 {0x4F60,0x4F60,0x4F60,'h','e','l','l','o',0}, 9, 9
816 /* tests UTF-7 followed by characters that should be encoded but aren't */
818 "+T2BZ-\x82\xFE", -1, output, ARRAY_SIZE(output) - 1,
819 {0x4F60,0x0082,0x00FE,0}, 4, 4
821 /* tests srclen > strlen(src) */
823 "a\0b", 4, output, ARRAY_SIZE(output) - 1,
824 {'a',0,'b',0}, 4, 4
826 /* tests srclen < strlen(src) outside of a UTF-7 sequence */
828 "hello", 2, output, ARRAY_SIZE(output) - 1,
829 {'h','e'}, 2, 2
831 /* tests srclen < strlen(src) inside of a UTF-7 sequence */
833 "+T2BZfQ-", 4, output, ARRAY_SIZE(output) - 1,
834 {0x4F60}, 1, 1
836 /* tests srclen < strlen(src) right at the beginning of a UTF-7 sequence */
838 "hi+T2A-", 3, output, ARRAY_SIZE(output) - 1,
839 {'h','i'}, 2, 2
841 /* tests srclen < strlen(src) right at the end of a UTF-7 sequence */
843 "+T2A-hi", 5, output, ARRAY_SIZE(output) - 1,
844 {0x4F60}, 1, 1
846 /* tests srclen < strlen(src) at the beginning of an escaped + sign */
848 "hi+-", 3, output, ARRAY_SIZE(output) - 1,
849 {'h','i'}, 2, 2
851 /* tests srclen < strlen(src) at the end of an escaped + sign */
853 "+-hi", 2, output, ARRAY_SIZE(output) - 1,
854 {'+'}, 1, 1
856 /* tests len=0 but no error */
858 "+", 1, output, ARRAY_SIZE(output) - 1,
859 {}, 0, 0
861 /* tests a single null char */
863 "", -1, output, ARRAY_SIZE(output) - 1,
864 {0}, 1, 1
866 /* tests a buffer that runs out while not decoding a UTF-7 sequence */
868 "hello", -1, output, 2,
869 {'h','e'}, 2, 0
871 /* tests a buffer that runs out in the middle of decoding a UTF-7 sequence */
873 "+T2BZfQ-", -1, output, 1,
874 {0x4F60}, 1, 0
878 /* test which one-byte characters remove stray + signs */
879 for (i = 0; i < 256; i++)
881 sprintf(input, "+%c+AAA", i);
883 memset(output, 0x23, sizeof(output) - sizeof(WCHAR));
884 output[ARRAY_SIZE(output) - 1] = 0;
886 len = MultiByteToWideChar(CP_UTF7, 0, input, 7, output, ARRAY_SIZE(output) - 1);
888 if (i == '-')
890 /* removes the - sign */
891 expected_len = 3;
892 expected[0] = 0x002B;
893 expected[1] = 0;
894 expected[2] = 0;
896 else if (i <= 0x7F && base64_decoding_table[i] != -1)
898 /* absorbs the character into the base64 sequence */
899 expected_len = 2;
900 expected[0] = (base64_decoding_table[i] << 10) | 0x03E0;
901 expected[1] = 0;
903 else
905 /* removes the + sign */
906 expected_len = 3;
907 expected[0] = i;
908 expected[1] = 0;
909 expected[2] = 0;
911 expected[expected_len] = 0x2323;
913 ok(len == expected_len, "i=0x%02x: expected len=%i, got len=%i\n", i, expected_len, len);
914 ok(memcmp(output, expected, (expected_len + 1) * sizeof(WCHAR)) == 0,
915 "i=0x%02x: expected output=%s, got output=%s\n",
916 i, wine_dbgstr_wn(expected, expected_len + 1), wine_dbgstr_wn(output, expected_len + 1));
919 /* test which one-byte characters terminate a sequence
920 * also test whether the unfinished byte pair is discarded or not */
921 for (i = 0; i < 256; i++)
923 sprintf(input, "+B%c+AAA", i);
925 memset(output, 0x23, sizeof(output) - sizeof(WCHAR));
926 output[ARRAY_SIZE(output) - 1] = 0;
928 len = MultiByteToWideChar(CP_UTF7, 0, input, 8, output, ARRAY_SIZE(output) - 1);
930 if (i == '-')
932 /* explicitly terminates */
933 expected_len = 2;
934 expected[0] = 0;
935 expected[1] = 0;
937 else if (i <= 0x7F)
939 if (base64_decoding_table[i] != -1)
941 /* absorbs the character into the base64 sequence */
942 expected_len = 3;
943 expected[0] = 0x0400 | (base64_decoding_table[i] << 4) | 0x000F;
944 expected[1] = 0x8000;
945 expected[2] = 0;
947 else
949 /* implicitly terminates and discards the unfinished byte pair */
950 expected_len = 3;
951 expected[0] = i;
952 expected[1] = 0;
953 expected[2] = 0;
956 else
958 /* implicitly terminates but does not the discard unfinished byte pair */
959 expected_len = 3;
960 expected[0] = i;
961 expected[1] = 0x0400;
962 expected[2] = 0;
964 expected[expected_len] = 0x2323;
966 ok(len == expected_len, "i=0x%02x: expected len=%i, got len=%i\n", i, expected_len, len);
967 ok(memcmp(output, expected, (expected_len + 1) * sizeof(WCHAR)) == 0,
968 "i=0x%02x: expected output=%s, got output=%s\n",
969 i, wine_dbgstr_wn(expected, expected_len + 1), wine_dbgstr_wn(output, expected_len + 1));
972 for (i = 0; i < ARRAY_SIZE(tests); i++)
974 memset(output, 0x23, sizeof(output) - sizeof(WCHAR));
975 output[ARRAY_SIZE(output) - 1] = 0;
976 SetLastError(0xdeadbeef);
978 len = MultiByteToWideChar(CP_UTF7, 0, tests[i].src, tests[i].srclen,
979 tests[i].dst, tests[i].dstlen);
981 tests[i].expected_dst[tests[i].chars_written] = 0x2323;
983 if (!tests[i].len && tests[i].chars_written)
985 ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
986 "tests[%i]: expected error=0x%x, got error=0x%x\n",
987 i, ERROR_INSUFFICIENT_BUFFER, GetLastError());
989 ok(len == tests[i].len, "tests[%i]: expected len=%i, got len=%i\n", i, tests[i].len, len);
991 if (tests[i].dst)
993 ok(memcmp(tests[i].dst, tests[i].expected_dst, (tests[i].chars_written + 1) * sizeof(WCHAR)) == 0,
994 "tests[%i]: expected dst=%s, got dst=%s\n",
995 i, wine_dbgstr_wn(tests[i].expected_dst, tests[i].chars_written + 1),
996 wine_dbgstr_wn(tests[i].dst, tests[i].chars_written + 1));
1001 static void test_undefined_byte_char(void)
1003 static const struct tag_testset {
1004 UINT codepage;
1005 LPCSTR str;
1006 BOOL is_error;
1007 } testset[] = {
1008 { 37, "\x6f", FALSE },
1009 { 874, "\xdd", TRUE },
1010 { 932, "\xfe", TRUE },
1011 { 932, "\x80", FALSE },
1012 { 932, "\x81\x45", FALSE },
1013 { 936, "\xff", TRUE },
1014 { 949, "\xff", TRUE },
1015 { 950, "\xff", TRUE },
1016 { 1252, "?", FALSE },
1017 { 1252, "\x90", FALSE },
1018 { 1253, "\xaa", TRUE },
1019 { 1255, "\xff", TRUE },
1020 { 1257, "\xa5", TRUE },
1022 INT i, ret;
1023 DWORD err;
1025 for (i = 0; i < ARRAY_SIZE(testset); i++) {
1026 if (! IsValidCodePage(testset[i].codepage))
1028 skip("Codepage %d not available\n", testset[i].codepage);
1029 continue;
1032 SetLastError(0xdeadbeef);
1033 ret = MultiByteToWideChar(testset[i].codepage, MB_ERR_INVALID_CHARS,
1034 testset[i].str, -1, NULL, 0);
1035 err = GetLastError();
1036 if (testset[i].is_error) {
1037 ok(err == ERROR_NO_UNICODE_TRANSLATION, "Test %u: err is %u\n", i, err);
1038 ok(ret == 0, "Test %u: ret is %d\n", i, ret);
1040 else {
1041 ok(err == 0xdeadbeef, "Test %u: err is %u\n", i, err);
1042 ok(ret == 2, "Test %u: ret is %d\n", i, ret);
1045 SetLastError(0xdeadbeef);
1046 ret = MultiByteToWideChar(testset[i].codepage, 0,
1047 testset[i].str, -1, NULL, 0);
1048 err = GetLastError();
1049 ok(err == 0xdeadbeef, "Test %u: err is %u\n", i, err);
1050 ok(ret == 2, "Test %u: ret is %d\n", i, ret);
1054 static void test_threadcp(void)
1056 static const LCID ENGLISH = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
1057 static const LCID HINDI = MAKELCID(MAKELANGID(LANG_HINDI, SUBLANG_HINDI_INDIA), SORT_DEFAULT);
1058 static const LCID GEORGIAN = MAKELCID(MAKELANGID(LANG_GEORGIAN, SUBLANG_GEORGIAN_GEORGIA), SORT_DEFAULT);
1059 static const LCID RUSSIAN = MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_RUSSIAN_RUSSIA), SORT_DEFAULT);
1060 static const LCID JAPANESE = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), SORT_DEFAULT);
1061 static const LCID CHINESE = MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT);
1063 BOOL islead, islead_default;
1064 CPINFOEXA cpi;
1065 UINT cp, acp;
1066 int i, num;
1067 LCID last;
1068 BOOL ret;
1070 struct test {
1071 LCID lcid;
1072 UINT threadcp;
1073 } lcids[] = {
1074 { HINDI, 0 },
1075 { GEORGIAN, 0 },
1076 { ENGLISH, 1252 },
1077 { RUSSIAN, 1251 },
1078 { JAPANESE, 932 },
1079 { CHINESE, 936 }
1082 struct test_islead_nocp {
1083 LCID lcid;
1084 BYTE testchar;
1085 } isleads_nocp[] = {
1086 { HINDI, 0x00 },
1087 { HINDI, 0x81 },
1088 { HINDI, 0xa0 },
1089 { HINDI, 0xe0 },
1091 { GEORGIAN, 0x00 },
1092 { GEORGIAN, 0x81 },
1093 { GEORGIAN, 0xa0 },
1094 { GEORGIAN, 0xe0 },
1097 struct test_islead {
1098 LCID lcid;
1099 BYTE testchar;
1100 BOOL islead;
1101 } isleads[] = {
1102 { ENGLISH, 0x00, FALSE },
1103 { ENGLISH, 0x81, FALSE },
1104 { ENGLISH, 0xa0, FALSE },
1105 { ENGLISH, 0xe0, FALSE },
1107 { RUSSIAN, 0x00, FALSE },
1108 { RUSSIAN, 0x81, FALSE },
1109 { RUSSIAN, 0xa0, FALSE },
1110 { RUSSIAN, 0xe0, FALSE },
1112 { JAPANESE, 0x00, FALSE },
1113 { JAPANESE, 0x81, TRUE },
1114 { JAPANESE, 0xa0, FALSE },
1115 { JAPANESE, 0xe0, TRUE },
1117 { CHINESE, 0x00, FALSE },
1118 { CHINESE, 0x81, TRUE },
1119 { CHINESE, 0xa0, TRUE },
1120 { CHINESE, 0xe0, TRUE },
1123 last = GetThreadLocale();
1124 acp = GetACP();
1126 for (i = 0; i < ARRAY_SIZE(lcids); i++)
1128 SetThreadLocale(lcids[i].lcid);
1130 cp = 0xdeadbeef;
1131 GetLocaleInfoA(lcids[i].lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER, (LPSTR)&cp, sizeof(cp));
1132 ok(cp == lcids[i].threadcp, "wrong codepage %u for lcid %04x, should be %u\n", cp, lcids[i].lcid, lcids[i].threadcp);
1134 /* GetCPInfoEx/GetCPInfo - CP_ACP */
1135 SetLastError(0xdeadbeef);
1136 memset(&cpi, 0, sizeof(cpi));
1137 ret = GetCPInfoExA(CP_ACP, 0, &cpi);
1138 ok(ret, "GetCPInfoExA failed for lcid %04x, error %d\n", lcids[i].lcid, GetLastError());
1139 ok(cpi.CodePage == acp, "wrong codepage %u for lcid %04x, should be %u\n", cpi.CodePage, lcids[i].lcid, acp);
1141 /* WideCharToMultiByte - CP_ACP */
1142 num = WideCharToMultiByte(CP_ACP, 0, foobarW, -1, NULL, 0, NULL, NULL);
1143 ok(num == 7, "ret is %d (%04x)\n", num, lcids[i].lcid);
1145 /* MultiByteToWideChar - CP_ACP */
1146 num = MultiByteToWideChar(CP_ACP, 0, "foobar", -1, NULL, 0);
1147 ok(num == 7, "ret is %d (%04x)\n", num, lcids[i].lcid);
1149 /* GetCPInfoEx/GetCPInfo - CP_THREAD_ACP */
1150 SetLastError(0xdeadbeef);
1151 memset(&cpi, 0, sizeof(cpi));
1152 ret = GetCPInfoExA(CP_THREAD_ACP, 0, &cpi);
1153 ok(ret, "GetCPInfoExA failed for lcid %04x, error %d\n", lcids[i].lcid, GetLastError());
1154 if (lcids[i].threadcp)
1155 ok(cpi.CodePage == lcids[i].threadcp, "wrong codepage %u for lcid %04x, should be %u\n",
1156 cpi.CodePage, lcids[i].lcid, lcids[i].threadcp);
1157 else
1158 ok(cpi.CodePage == acp || cpi.CodePage == CP_UTF8 /* Win10 1809+ */,
1159 "wrong codepage %u for lcid %04x, should be %u\n", cpi.CodePage, lcids[i].lcid, acp);
1161 /* WideCharToMultiByte - CP_THREAD_ACP */
1162 num = WideCharToMultiByte(CP_THREAD_ACP, 0, foobarW, -1, NULL, 0, NULL, NULL);
1163 ok(num == 7, "ret is %d (%04x)\n", num, lcids[i].lcid);
1165 /* MultiByteToWideChar - CP_THREAD_ACP */
1166 num = MultiByteToWideChar(CP_THREAD_ACP, 0, "foobar", -1, NULL, 0);
1167 ok(num == 7, "ret is %d (%04x)\n", num, lcids[i].lcid);
1170 /* IsDBCSLeadByteEx - locales without codepage */
1171 for (i = 0; i < ARRAY_SIZE(isleads_nocp); i++)
1173 SetThreadLocale(isleads_nocp[i].lcid);
1175 GetCPInfoExA(CP_THREAD_ACP, 0, &cpi);
1176 islead_default = IsDBCSLeadByteEx(cpi.CodePage, isleads_nocp[i].testchar);
1177 islead = IsDBCSLeadByteEx(CP_THREAD_ACP, isleads_nocp[i].testchar);
1179 ok(islead == islead_default, "wrong islead %i for test char %x in lcid %04x. should be %i\n",
1180 islead, isleads_nocp[i].testchar, isleads_nocp[i].lcid, islead_default);
1183 /* IsDBCSLeadByteEx - locales with codepage */
1184 for (i = 0; i < ARRAY_SIZE(isleads); i++)
1186 SetThreadLocale(isleads[i].lcid);
1188 islead = IsDBCSLeadByteEx(CP_THREAD_ACP, isleads[i].testchar);
1189 ok(islead == isleads[i].islead, "wrong islead %i for test char %x in lcid %04x. should be %i\n",
1190 islead, isleads[i].testchar, isleads[i].lcid, isleads[i].islead);
1193 SetThreadLocale(last);
1196 static void test_dbcs_to_widechar(void)
1198 int i, count, count2;
1199 WCHAR wbuf[5];
1200 unsigned char buf[] = {0xbf, 0xb4, 0xc7, '\0', 'x'};
1201 static const DWORD flags[] = {
1202 MB_PRECOMPOSED,
1203 MB_COMPOSITE,
1205 MB_PRECOMPOSED|MB_USEGLYPHCHARS,
1206 MB_COMPOSITE |MB_USEGLYPHCHARS,
1208 MB_PRECOMPOSED|MB_ERR_INVALID_CHARS,
1209 MB_COMPOSITE |MB_ERR_INVALID_CHARS,
1211 MB_PRECOMPOSED|MB_ERR_INVALID_CHARS|MB_USEGLYPHCHARS,
1212 MB_COMPOSITE |MB_ERR_INVALID_CHARS|MB_USEGLYPHCHARS,
1215 for (i = 0; i < ARRAY_SIZE(flags); ++i)
1217 memset(wbuf, 0xff, sizeof(wbuf));
1218 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 2, NULL, 0);
1219 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 2, wbuf, count);
1221 ok(count == 1, "%04x: returned %d (expected 1)\n", flags[i], count);
1222 ok(count2 == 1, "%04x: returned %d (expected 1)\n", flags[i], count2);
1223 ok(wbuf[0] == 0x770b, "%04x: returned %04x (expected 770b)\n", flags[i], wbuf[0]);
1224 ok(wbuf[1] == 0xffff, "%04x: returned %04x (expected ffff)\n", flags[i], wbuf[1]);
1227 for (i = 0; i < ARRAY_SIZE(flags); ++i)
1229 memset(wbuf, 0xff, sizeof(wbuf));
1230 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 3, NULL, 0);
1231 SetLastError( 0xdeadbeef );
1232 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 3, wbuf, count);
1234 if (flags[i] & MB_ERR_INVALID_CHARS)
1236 ok(count == 0, "%04x: returned %d (expected 0)\n", flags[i], count);
1237 ok(count2 == 0, "%04x: returned %d (expected 0)\n", flags[i], count2);
1238 ok(GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%04x: returned %d (expected %d)\n",
1239 flags[i], GetLastError(), ERROR_NO_UNICODE_TRANSLATION);
1241 else
1243 ok(count == 2, "%04x: returned %d (expected 2)\n", flags[i], count);
1244 ok(count2 == 2, "%04x: returned %d (expected 2)\n", flags[i], count2);
1245 ok(wbuf[0] == 0x770b, "%04x: returned %04x (expected 770b)\n", flags[i], wbuf[0]);
1246 ok(wbuf[1] == 0x003f || broken(wbuf[1] == 0), /*windows xp*/
1247 "%04x: wrong wide char: %04x\n", flags[i], wbuf[1]);
1248 ok(wbuf[2] == 0xffff, "%04x: returned %04x (expected ffff)\n", flags[i], wbuf[2]);
1252 /* src ends with null character */
1253 for (i = 0; i < ARRAY_SIZE(flags); ++i)
1255 memset(wbuf, 0xff, sizeof(wbuf));
1256 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 4, NULL, 0);
1257 SetLastError( 0xdeadbeef );
1258 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 4, wbuf, count);
1259 ok(count == count2, "%04x: returned %d (expected %d)\n", flags[i], count2, count);
1261 if (flags[i] & MB_ERR_INVALID_CHARS)
1263 ok(count == 0, "%04x: returned %d (expected 0)\n", flags[i], count);
1264 ok(GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%04x: returned %d (expected %d)\n",
1265 flags[i], GetLastError(), ERROR_NO_UNICODE_TRANSLATION);
1267 else
1269 WCHAR wbuf_ok[] = { 0x770b, 0x003f, '\0', 0xffff };
1270 WCHAR wbuf_broken[] = { 0x770b, '\0', 0xffff, 0xffff };
1271 ok(count == 3 || broken(count == 2 /*windows xp*/),
1272 "%04x: returned %d (expected 3)\n", flags[i], count);
1273 ok(!memcmp(wbuf, wbuf_ok, sizeof(wbuf_ok))
1274 || broken(!memcmp(wbuf, wbuf_broken, sizeof(wbuf_broken))),
1275 "%04x: returned %04x %04x %04x %04x (expected %04x %04x %04x %04x)\n",
1276 flags[i], wbuf[0], wbuf[1], wbuf[2], wbuf[3],
1277 wbuf_ok[0], wbuf_ok[1], wbuf_ok[2], wbuf_ok[3]);
1281 /* src has null character, but not ends with it */
1282 for (i = 0; i < ARRAY_SIZE(flags); ++i)
1284 memset(wbuf, 0xff, sizeof(wbuf));
1285 count = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 5, NULL, 0);
1286 SetLastError( 0xdeadbeef );
1287 count2 = MultiByteToWideChar(936, flags[i], (char*)&buf[0], 5, wbuf, count);
1288 ok(count == count2, "%04x: returned %d (expected %d)\n", flags[i], count2, count);
1290 if (flags[i] & MB_ERR_INVALID_CHARS)
1292 ok(count == 0, "%04x: returned %d (expected 0)\n", flags[i], count);
1293 ok(GetLastError() == ERROR_NO_UNICODE_TRANSLATION, "%04x: returned %d (expected %d)\n",
1294 flags[i], GetLastError(), ERROR_NO_UNICODE_TRANSLATION);
1296 else
1298 WCHAR wbuf_ok[] = { 0x770b, 0x003f, '\0', 'x', 0xffff };
1299 WCHAR wbuf_broken[] = { 0x770b, '\0', 'x', 0xffff, 0xffff };
1300 ok(count == 4 || broken(count == 3),
1301 "%04x: returned %d (expected 4)\n", flags[i], count);
1302 ok(!memcmp(wbuf, wbuf_ok, sizeof(wbuf_ok))
1303 || broken(!memcmp(wbuf, wbuf_broken, sizeof(wbuf_broken))),
1304 "%04x: returned %04x %04x %04x %04x %04x (expected %04x %04x %04x %04x %04x)\n",
1305 flags[i], wbuf[0], wbuf[1], wbuf[2], wbuf[3], wbuf[4],
1306 wbuf_ok[0], wbuf_ok[1], wbuf_ok[2], wbuf_ok[3], wbuf_ok[4]);
1311 START_TEST(codepage)
1313 BOOL bUsedDefaultChar;
1315 test_destination_buffer();
1316 test_null_source();
1317 test_negative_source_length();
1318 test_negative_dest_length();
1319 test_other_invalid_parameters();
1320 test_overlapped_buffers();
1322 /* WideCharToMultiByte has two code paths, test both here */
1323 test_string_conversion(NULL);
1324 test_string_conversion(&bUsedDefaultChar);
1326 test_utf7_encoding();
1327 test_utf7_decoding();
1329 test_undefined_byte_char();
1330 test_threadcp();
1332 test_dbcs_to_widechar();