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 }
503