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