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