[KERNEL32_APITEST] Check CJK codepage before tests
[reactos.git] / modules / rostests / apitests / kernel32 / ConsoleCP.c
1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Test for i18n console test
5 * PROGRAMMERS: Katayama Hirofumi MZ
6 */
7
8 #include "precomp.h"
9
10 #define okCURSOR(hCon, c) do { \
11 CONSOLE_SCREEN_BUFFER_INFO __sbi; \
12 BOOL expect = GetConsoleScreenBufferInfo((hCon), &__sbi) && \
13 __sbi.dwCursorPosition.X == (c).X && __sbi.dwCursorPosition.Y == (c).Y; \
14 ok(expect, "Expected cursor at (%d,%d), got (%d,%d)\n", \
15 (c).X, (c).Y, __sbi.dwCursorPosition.X, __sbi.dwCursorPosition.Y); \
16 } while (0)
17
18 #define ATTR \
19 (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED)
20
21 static const WCHAR u0414[] = { 0x0414, 0 }; /* Д */
22 static const WCHAR u9580[] = { 0x9580, 0 }; /* 門 */
23 static const WCHAR ideograph_space = (WCHAR)0x3000; /* fullwidth space */
24 LCID lcidJapanese = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), SORT_DEFAULT);
25 LCID lcidRussian = MAKELCID(MAKELANGID(LANG_RUSSIAN , SUBLANG_DEFAULT), SORT_DEFAULT);
26
27 static BOOL IsCJKCodePage(void)
28 {
29 switch (GetOEMCP())
30 {
31 case 936: // Chinese PRC
32 case 932: // Japanese
33 case 949: // Korean
34 case 1361: // Korean (Johab)
35 case 950: // Taiwan
36 return TRUE;
37 }
38 return FALSE;
39 }
40
41 /* Russian Code Page 855 */
42 // NOTE that CP 866 can also be used
43 static void test_cp855(HANDLE hConOut)
44 {
45 BOOL ret;
46 DWORD oldcp;
47 int n;
48 DWORD len;
49 COORD c;
50 CONSOLE_SCREEN_BUFFER_INFO csbi;
51 int count;
52 WCHAR str[32];
53 WORD attr;
54
55 if (!IsValidCodePage(855))
56 {
57 skip("Codepage 855 not available\n");
58 return;
59 }
60
61 /* Set code page */
62 oldcp = GetConsoleOutputCP();
63 SetLastError(0xdeadbeef);
64 ret = SetConsoleOutputCP(855);
65 if (!ret)
66 {
67 skip("SetConsoleOutputCP failed with last error %lu\n", GetLastError());
68 return;
69 }
70
71 /* Get info */
72 ret = GetConsoleScreenBufferInfo(hConOut, &csbi);
73 ok(ret, "GetConsoleScreenBufferInfo failed\n");
74 trace("csbi.dwSize.X:%d, csbi.dwSize.Y:%d\n", csbi.dwSize.X, csbi.dwSize.Y);
75 count = csbi.dwSize.X * 3 / 2;
76 trace("count: %d\n", count);
77
78 /* "\u0414" */
79 {
80 /* Output u0414 "count" times at (0,0) */
81 c.X = c.Y = 0;
82 SetConsoleCursorPosition(hConOut, c);
83 okCURSOR(hConOut, c);
84 for (n = 0; n < count; ++n)
85 {
86 ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
87 ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
88 }
89
90 /* Check cursor */
91 len = count; /* u0414 is normal width in Russian */
92 c.X = (SHORT)(len % csbi.dwSize.X);
93 c.Y = (SHORT)(len / csbi.dwSize.X);
94 okCURSOR(hConOut, c);
95
96 /* Read characters at (0,0) */
97 c.X = c.Y = 0;
98 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
99 ok(ret, "ReadConsoleOutputCharacterW failed\n");
100 ok(len == 6, "len was: %ld\n", len);
101 ok(str[0] == 0x414, "str[0] was: 0x%04X\n", str[0]);
102 ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
103 ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
104
105 /* Check cursor */
106 c.X = 1;
107 c.Y = 0;
108 ret = SetConsoleCursorPosition(hConOut, c);
109 ok(ret, "SetConsoleCursorPosition failed\n");
110 okCURSOR(hConOut, c);
111
112 /* Fill by space */
113 c.X = c.Y = 0;
114 FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
115
116 /* Output u0414 "count" times at (1,0) */
117 c.X = 1;
118 c.Y = 0;
119 SetConsoleCursorPosition(hConOut, c);
120 okCURSOR(hConOut, c);
121 for (n = 0; n < count; ++n)
122 {
123 ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
124 ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
125 }
126
127 /* Check cursor */
128 len = 1 + count;
129 c.X = (SHORT)(len % csbi.dwSize.X);
130 c.Y = (SHORT)(len / csbi.dwSize.X);
131 okCURSOR(hConOut, c);
132
133 /* Read characters at (0,0) */
134 c.X = c.Y = 0;
135 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
136 ok(ret, "ReadConsoleOutputCharacterW failed\n");
137 ok(len == 6, "len was: %ld\n", len);
138 ok(str[0] == L' ', "str[0] was: 0x%04X\n", str[0]);
139 ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
140 ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
141 }
142
143 /* "\u9580" */
144 {
145 /* Output u9580 "count" times at (0,0) */
146 c.X = c.Y = 0;
147 SetConsoleCursorPosition(hConOut, c);
148 okCURSOR(hConOut, c);
149 for (n = 0; n < count; ++n)
150 {
151 ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
152 ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
153 }
154
155 /* Check cursor */
156 len = count; /* u9580 is normal width in Russian */
157 c.X = (SHORT)(len % csbi.dwSize.X);
158 c.Y = (SHORT)(len / csbi.dwSize.X);
159 okCURSOR(hConOut, c);
160
161 /* Check cursor */
162 c.X = 1;
163 c.Y = 0;
164 ret = SetConsoleCursorPosition(hConOut, c);
165 ok(ret, "SetConsoleCursorPosition failed\n");
166 okCURSOR(hConOut, c);
167
168 /* Fill by space */
169 c.X = c.Y = 0;
170 ret = FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
171 ok(ret, "FillConsoleOutputCharacterW failed\n");
172 ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
173
174 /* Output u9580 "count" times at (1,0) */
175 c.X = 1;
176 c.Y = 0;
177 SetConsoleCursorPosition(hConOut, c);
178 okCURSOR(hConOut, c);
179 for (n = 0; n < count; ++n)
180 {
181 ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
182 ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
183 }
184
185 /* Check cursor */
186 len = 1 + count;
187 c.X = (SHORT)(len % csbi.dwSize.X);
188 c.Y = (SHORT)(len / csbi.dwSize.X);
189 okCURSOR(hConOut, c);
190
191 /* Fill by ideograph space */
192 c.X = c.Y = 0;
193 ret = FillConsoleOutputCharacterW(hConOut, ideograph_space, csbi.dwSize.X * csbi.dwSize.Y, c, &len);
194 ok(ret, "FillConsoleOutputCharacterW failed\n");
195 ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
196
197 /* Read characters at (0,0) */
198 c.X = c.Y = 0;
199 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
200 ok(ret, "ReadConsoleOutputCharacterW failed\n");
201 ok(len == 6, "len was: %ld\n", len);
202 ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
203 ok(str[1] == ideograph_space || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
204 ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
205
206 /* Read attr at (0,0) */
207 c.X = c.Y = 0;
208 ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
209 ok(ret, "ReadConsoleOutputAttribute failed\n");
210 ok(attr == ATTR, "attr was: %d\n", attr);
211 ok(len == 1, "len was %ld\n", len);
212
213 /* Read characters at (1,0) */
214 c.X = 1;
215 c.Y = 0;
216 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
217 ok(ret, "ReadConsoleOutputCharacterW failed\n");
218 ok(len == 6, "len was: %ld\n", len);
219 ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
220 ok(str[1] == ideograph_space || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
221 ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
222
223 /* Output u9580 "count" once at (1,0) */
224 c.X = 1;
225 c.Y = 0;
226 SetConsoleCursorPosition(hConOut, c);
227 okCURSOR(hConOut, c);
228 ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
229 ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
230
231 /* Read attr (1,0) */
232 c.X = 1;
233 c.Y = 0;
234 ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
235 ok(ret, "ReadConsoleOutputAttribute failed\n");
236 ok(attr == ATTR, "attr was: %d\n", attr);
237 ok(len == 1, "len was %ld\n", len);
238
239 /* Check cursor */
240 c.X = 2;
241 c.Y = 0;
242 okCURSOR(hConOut, c);
243
244 /* Read characters at (0,0) */
245 c.X = c.Y = 0;
246 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
247 ok(ret, "ReadConsoleOutputCharacterW failed\n");
248 ok(len == 6, "len was: %ld\n", len);
249 ok(str[0] == ideograph_space || str[0] == L'?', "str[0] was: 0x%04X\n", str[0]);
250 ok(str[1] == 0x9580 || str[1] == L'?', "str[1] was: 0x%04X\n", str[1]);
251 ok(str[2] == ideograph_space || str[2] == L'?', "str[2] was: 0x%04X\n", str[2]);
252 }
253
254 /* Restore code page */
255 SetConsoleOutputCP(oldcp);
256 }
257
258 /* Japanese Code Page 932 */
259 static void test_cp932(HANDLE hConOut)
260 {
261 BOOL ret;
262 DWORD oldcp;
263 int n;
264 DWORD len;
265 COORD c;
266 CONSOLE_SCREEN_BUFFER_INFO csbi;
267 int count;
268 WCHAR str[32];
269 WORD attr;
270
271 if (!IsValidCodePage(932))
272 {
273 skip("Codepage 932 not available\n");
274 return;
275 }
276
277 /* Set code page */
278 oldcp = GetConsoleOutputCP();
279 SetLastError(0xdeadbeef);
280 ret = SetConsoleOutputCP(932);
281 if (!ret)
282 {
283 skip("SetConsoleOutputCP failed with last error %lu\n", GetLastError());
284 return;
285 }
286
287 /* Get info */
288 ret = GetConsoleScreenBufferInfo(hConOut, &csbi);
289 ok(ret, "GetConsoleScreenBufferInfo failed\n");
290 trace("csbi.dwSize.X:%d, csbi.dwSize.Y:%d\n", csbi.dwSize.X, csbi.dwSize.Y);
291 count = csbi.dwSize.X * 3 / 2;
292 trace("count: %d\n", count);
293
294 /* "\u0414" */
295 {
296 /* Output u0414 "count" times at (0,0) */
297 c.X = c.Y = 0;
298 SetConsoleCursorPosition(hConOut, c);
299 okCURSOR(hConOut, c);
300 for (n = 0; n < count; ++n)
301 {
302 ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
303 ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
304 }
305
306 /* Check cursor */
307 GetConsoleScreenBufferInfo(hConOut, &csbi);
308 len = count * 2; /* u0414 is fullwidth in Japanese */
309 c.X = (SHORT)(len % csbi.dwSize.X);
310 c.Y = (SHORT)(len / csbi.dwSize.X);
311 okCURSOR(hConOut, c);
312
313 /* Read characters at (0,0) */
314 c.X = c.Y = 0;
315 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
316 ok(ret, "ReadConsoleOutputCharacterW failed\n");
317 ok(len == 3, "len was: %ld\n", len);
318 ok(str[0] == 0x414, "str[0] was: 0x%04X\n", str[0]);
319 ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
320 ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
321
322 /* Check cursor */
323 c.X = 1;
324 c.Y = 0;
325 ret = SetConsoleCursorPosition(hConOut, c);
326 ok(ret, "SetConsoleCursorPosition failed\n");
327 okCURSOR(hConOut, c);
328
329 /* Fill by space */
330 c.X = c.Y = 0;
331 FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
332
333 /* Output u0414 "count" times at (1,0) */
334 c.X = 1;
335 c.Y = 0;
336 SetConsoleCursorPosition(hConOut, c);
337 okCURSOR(hConOut, c);
338 for (n = 0; n < count; ++n)
339 {
340 ret = WriteConsoleW(hConOut, u0414, lstrlenW(u0414), &len, NULL);
341 ok(ret && len == lstrlenW(u0414), "WriteConsoleW failed\n");
342 }
343
344 /* Check cursor */
345 len = csbi.dwSize.X + (count - (csbi.dwSize.X - 1) / 2) * 2;
346 c.X = (SHORT)(len % csbi.dwSize.X);
347 c.Y = (SHORT)(len / csbi.dwSize.X);
348 okCURSOR(hConOut, c);
349
350 /* Read characters at (0,0) */
351 c.X = 0;
352 c.Y = 0;
353 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
354 ok(ret, "ReadConsoleOutputCharacterW failed\n");
355 ok(len == 4, "len was: %ld\n", len);
356 ok(str[0] == L' ', "str[0] was: 0x%04X\n", str[0]);
357 ok(str[1] == 0x414, "str[1] was: 0x%04X\n", str[1]);
358 ok(str[2] == 0x414, "str[2] was: 0x%04X\n", str[2]);
359 }
360
361 /* "\u9580" */
362 {
363 /* Output u9580 "count" times at (0,0) */
364 c.X = c.Y = 0;
365 SetConsoleCursorPosition(hConOut, c);
366 okCURSOR(hConOut, c);
367 for (n = 0; n < count; ++n)
368 {
369 ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
370 ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
371 }
372
373 /* Check cursor */
374 len = count * 2; /* u9580 is fullwidth in Japanese */
375 c.X = (SHORT)(len % csbi.dwSize.X);
376 c.Y = (SHORT)(len / csbi.dwSize.X);
377 okCURSOR(hConOut, c);
378
379 /* Check cursor */
380 c.X = 1;
381 c.Y = 0;
382 ret = SetConsoleCursorPosition(hConOut, c);
383 ok(ret, "SetConsoleCursorPosition failed\n");
384 okCURSOR(hConOut, c);
385
386 /* Fill by space */
387 c.X = c.Y = 0;
388 ret = FillConsoleOutputCharacterW(hConOut, L' ', csbi.dwSize.X * csbi.dwSize.Y, c, &len);
389 ok(ret, "FillConsoleOutputCharacterW failed\n");
390 ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
391
392 /* Output u9580 "count" times at (1,0) */
393 c.X = 1;
394 c.Y = 0;
395 SetConsoleCursorPosition(hConOut, c);
396 okCURSOR(hConOut, c);
397 for (n = 0; n < count; ++n)
398 {
399 ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
400 ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
401 }
402
403 /* Check cursor */
404 len = csbi.dwSize.X + (count - (csbi.dwSize.X - 1) / 2) * 2;
405 c.X = (SHORT)(len % csbi.dwSize.X);
406 c.Y = (SHORT)(len / csbi.dwSize.X);
407 okCURSOR(hConOut, c);
408
409 /* Fill by ideograph space */
410 c.X = c.Y = 0;
411 ret = FillConsoleOutputCharacterW(hConOut, ideograph_space, csbi.dwSize.X * csbi.dwSize.Y, c, &len);
412 ok(ret, "FillConsoleOutputCharacterW failed\n");
413 ok(len == csbi.dwSize.X * csbi.dwSize.Y, "len was: %ld\n", len);
414
415 /* Read characters at (0,0) */
416 c.X = c.Y = 0;
417 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
418 ok(ret, "ReadConsoleOutputCharacterW failed\n");
419 ok(len == 3, "len was: %ld\n", len);
420 ok(str[0] == ideograph_space, "str[0] was: 0x%04X\n", str[0]);
421 ok(str[1] == ideograph_space, "str[1] was: 0x%04X\n", str[1]);
422 ok(str[2] == ideograph_space, "str[2] was: 0x%04X\n", str[2]);
423
424 /* Read attr */
425 ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
426 ok(ret, "ReadConsoleOutputAttribute failed\n");
427 ok(attr == ATTR, "attr was: %d\n", attr);
428 ok(len == 1, "len was %ld\n", len);
429
430 /* Output u9580 "count" once at (1,0) */
431 c.X = 1;
432 c.Y = 0;
433 SetConsoleCursorPosition(hConOut, c);
434 okCURSOR(hConOut, c);
435 ret = WriteConsoleW(hConOut, u9580, lstrlenW(u9580), &len, NULL);
436 ok(ret && len == lstrlenW(u9580), "WriteConsoleW failed\n");
437
438 /* Read attr */
439 ret = ReadConsoleOutputAttribute(hConOut, &attr, 1, c, &len);
440 ok(ret, "ReadConsoleOutputAttribute failed\n");
441 ok(attr == ATTR, "attr was: %d\n", attr);
442 ok(len == 1, "len was %ld\n", len);
443
444 /* Check cursor */
445 c.X = 3;
446 c.Y = 0;
447 okCURSOR(hConOut, c);
448
449 /* Read characters */
450 c.X = c.Y = 0;
451 ret = ReadConsoleOutputCharacterW(hConOut, str, 3 * sizeof(WCHAR), c, &len);
452 ok(ret, "ReadConsoleOutputCharacterW failed\n");
453 ok(len == 4, "len was: %ld\n", len);
454 ok(str[0] == L' ', "str[0] was: 0x%04X\n", str[0]);
455 ok(str[1] == 0x9580, "str[1] was: 0x%04X\n", str[1]);
456 ok(str[2] == L' ', "str[2] was: 0x%04X\n", str[2]);
457 }
458
459 /* Restore code page */
460 SetConsoleOutputCP(oldcp);
461 }
462
463 START_TEST(ConsoleCP)
464 {
465 HANDLE hConIn, hConOut;
466 FreeConsole();
467 ok(AllocConsole(), "Couldn't alloc console\n");
468
469 hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
470 hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
471 ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
472 ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
473
474 if (IsValidLocale(lcidRussian, LCID_INSTALLED))
475 {
476 if (!IsCJKCodePage())
477 test_cp855(hConOut);
478 else
479 skip("Russian testcase is skipped because of CJK\n");
480 }
481 else
482 {
483 skip("Russian locale is not installed\n");
484 }
485
486 if (IsValidLocale(lcidJapanese, LCID_INSTALLED))
487 {
488 if (IsCJKCodePage())
489 test_cp932(hConOut);
490 else
491 skip("Japanese testcase is skipped because of not CJK\n");
492 }
493 else
494 {
495 skip("Japanese locale is not installed\n");
496 }
497
498 CloseHandle(hConIn);
499 CloseHandle(hConOut);
500 FreeConsole();
501 ok(AllocConsole(), "Couldn't alloc console\n");
502 }