testConsoleBuf.cxx 29 KB
Newer Older
1 2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
3 4
#include "kwsysPrivate.h"

5 6 7 8 9 10
// Ignore Windows version levels defined by command-line flags.  This
// source needs access to all APIs available on the host in order for
// the test to run properly.  The test binary is not installed anyway.
#undef _WIN32_WINNT
#undef NTDDI_VERSION

11 12 13 14 15
#include KWSYS_HEADER(Encoding.hxx)

// Work-around CMake dependency scanning limitation.  This must
// duplicate the above list of headers.
#if 0
16
#include "Encoding.hxx.in"
17 18 19 20
#endif

#if defined(_WIN32)

21
#include <algorithm>
22
#include <iomanip>
23
#include <iostream>
24
#include <stdexcept>
25 26 27 28
#include <string.h>
#include <wchar.h>
#include <windows.h>

29 30 31
#include "testConsoleBuf.hxx"

#if defined(_MSC_VER) && _MSC_VER >= 1800
32
#define KWSYS_WINDOWS_DEPRECATED_GetVersion
33 34
#endif
// يونيكود
35 36
static const WCHAR UnicodeInputTestString[] =
  L"\u064A\u0648\u0646\u064A\u0643\u0648\u062F!";
37 38 39 40 41
static UINT TestCodepage = KWSYS_ENCODING_DEFAULT_CODEPAGE;

static const DWORD waitTimeout = 10 * 1000;
static STARTUPINFO startupInfo;
static PROCESS_INFORMATION processInfo;
42 43
static HANDLE beforeInputEvent;
static HANDLE afterOutputEvent;
44 45 46
static std::string encodedInputTestString;
static std::string encodedTestString;

47 48
static void displayError(DWORD errorCode)
{
49 50 51
  std::cerr.setf(std::ios::hex, std::ios::basefield);
  std::cerr << "Failed with error: 0x" << errorCode << "!" << std::endl;
  LPWSTR message;
52 53 54 55 56
  if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                       FORMAT_MESSAGE_FROM_SYSTEM,
                     NULL, errorCode, 0, (LPWSTR)&message, 0, NULL)) {
    std::cerr << "Error message: " << kwsys::Encoding::ToNarrow(message)
              << std::endl;
57 58
    HeapFree(GetProcessHeap(), 0, message);
  } else {
59 60
    std::cerr << "FormatMessage() failed with error: 0x" << GetLastError()
              << "!" << std::endl;
61 62 63 64
  }
  std::cerr.unsetf(std::ios::hex);
}

65 66
std::basic_streambuf<char>* errstream(const char* unused)
{
67 68 69 70
  static_cast<void>(unused);
  return std::cerr.rdbuf();
}

71 72
std::basic_streambuf<wchar_t>* errstream(const wchar_t* unused)
{
73 74 75 76 77
  static_cast<void>(unused);
  return std::wcerr.rdbuf();
}

//----------------------------------------------------------------------------
78 79 80
template <typename T>
static void dumpBuffers(const T* expected, const T* received, size_t size)
{
81
  std::basic_ostream<T> err(errstream(expected));
82 83
  err << "Expected output: '" << std::basic_string<T>(expected, size) << "'"
      << std::endl;
84 85 86 87
  if (err.fail()) {
    err.clear();
    err << "--- Error while outputting ---" << std::endl;
  }
88 89
  err << "Received output: '" << std::basic_string<T>(received, size) << "'"
      << std::endl;
90 91 92 93 94 95
  if (err.fail()) {
    err.clear();
    err << "--- Error while outputting ---" << std::endl;
  }
  std::cerr << "Expected output | Received output" << std::endl;
  for (size_t i = 0; i < size; i++) {
96 97 98 99 100 101 102
    std::cerr << std::setbase(16) << std::setfill('0') << "     "
              << "0x" << std::setw(8) << static_cast<unsigned int>(expected[i])
              << " | "
              << "0x" << std::setw(8)
              << static_cast<unsigned int>(received[i]);
    if (static_cast<unsigned int>(expected[i]) !=
        static_cast<unsigned int>(received[i])) {
103 104 105 106
      std::cerr << "   MISMATCH!";
    }
    std::cerr << std::endl;
  }
107
  std::cerr << std::endl;
108 109
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
//----------------------------------------------------------------------------
static bool createProcess(HANDLE hIn, HANDLE hOut, HANDLE hErr)
{
  BOOL bInheritHandles = FALSE;
  DWORD dwCreationFlags = 0;
  memset(&processInfo, 0, sizeof(processInfo));
  memset(&startupInfo, 0, sizeof(startupInfo));
  startupInfo.cb = sizeof(startupInfo);
  startupInfo.dwFlags = STARTF_USESHOWWINDOW;
  startupInfo.wShowWindow = SW_HIDE;
  if (hIn || hOut || hErr) {
    startupInfo.dwFlags |= STARTF_USESTDHANDLES;
    startupInfo.hStdInput = hIn;
    startupInfo.hStdOutput = hOut;
    startupInfo.hStdError = hErr;
    bInheritHandles = TRUE;
  }

  WCHAR cmd[MAX_PATH];
  if (GetModuleFileNameW(NULL, cmd, MAX_PATH) == 0) {
    std::cerr << "GetModuleFileName failed!" << std::endl;
    return false;
  }
133 134 135 136
  WCHAR* p = cmd + wcslen(cmd);
  while (p > cmd && *p != L'\\')
    p--;
  *(p + 1) = 0;
137 138 139
  wcscat(cmd, cmdConsoleBufChild);
  wcscat(cmd, L".exe");

140 141 142 143 144 145 146 147 148 149 150 151
  bool success =
    CreateProcessW(NULL,            // No module name (use command line)
                   cmd,             // Command line
                   NULL,            // Process handle not inheritable
                   NULL,            // Thread handle not inheritable
                   bInheritHandles, // Set handle inheritance
                   dwCreationFlags,
                   NULL,         // Use parent's environment block
                   NULL,         // Use parent's starting directory
                   &startupInfo, // Pointer to STARTUPINFO structure
                   &processInfo) !=
    0; // Pointer to PROCESS_INFORMATION structure
152 153
  if (!success) {
    DWORD lastError = GetLastError();
154 155
    std::cerr << "CreateProcess(" << kwsys::Encoding::ToNarrow(cmd) << ")"
              << std::endl;
156
    displayError(lastError);
157 158 159 160 161 162 163 164
  }
  return success;
}

//----------------------------------------------------------------------------
static void finishProcess(bool success)
{
  if (success) {
165 166
    success =
      WaitForSingleObject(processInfo.hProcess, waitTimeout) == WAIT_OBJECT_0;
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
  };
  if (!success) {
    TerminateProcess(processInfo.hProcess, 1);
  }
  CloseHandle(processInfo.hProcess);
  CloseHandle(processInfo.hThread);
}

//----------------------------------------------------------------------------
static bool createPipe(PHANDLE readPipe, PHANDLE writePipe)
{
  SECURITY_ATTRIBUTES securityAttributes;
  securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  securityAttributes.bInheritHandle = TRUE;
  securityAttributes.lpSecurityDescriptor = NULL;
182 183
  return CreatePipe(readPipe, writePipe, &securityAttributes, 0) == 0 ? false
                                                                      : true;
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
}

//----------------------------------------------------------------------------
static void finishPipe(HANDLE readPipe, HANDLE writePipe)
{
  if (readPipe != INVALID_HANDLE_VALUE) {
    CloseHandle(readPipe);
  }
  if (writePipe != INVALID_HANDLE_VALUE) {
    CloseHandle(writePipe);
  }
}

//----------------------------------------------------------------------------
static HANDLE createFile(LPCWSTR fileName)
{
  SECURITY_ATTRIBUTES securityAttributes;
  securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  securityAttributes.bInheritHandle = TRUE;
  securityAttributes.lpSecurityDescriptor = NULL;

205 206 207 208 209 210 211
  HANDLE file =
    CreateFileW(fileName, GENERIC_READ | GENERIC_WRITE,
                0, // do not share
                &securityAttributes,
                CREATE_ALWAYS, // overwrite existing
                FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
                NULL); // no template
212
  if (file == INVALID_HANDLE_VALUE) {
213
    DWORD lastError = GetLastError();
214 215
    std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName) << ")"
              << std::endl;
216
    displayError(lastError);
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
  }
  return file;
}

//----------------------------------------------------------------------------
static void finishFile(HANDLE file)
{
  if (file != INVALID_HANDLE_VALUE) {
    CloseHandle(file);
  }
}

//----------------------------------------------------------------------------

#ifndef MAPVK_VK_TO_VSC
232
#define MAPVK_VK_TO_VSC (0)
233 234 235 236 237 238 239 240 241 242 243 244 245 246
#endif

static void writeInputKeyEvent(INPUT_RECORD inputBuffer[], WCHAR chr)
{
  inputBuffer[0].EventType = KEY_EVENT;
  inputBuffer[0].Event.KeyEvent.bKeyDown = TRUE;
  inputBuffer[0].Event.KeyEvent.wRepeatCount = 1;
  SHORT keyCode = VkKeyScanW(chr);
  if (keyCode == -1) {
    // Character can't be entered with current keyboard layout
    // Just set any, it doesn't really matter
    keyCode = 'K';
  }
  inputBuffer[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(keyCode);
247 248
  inputBuffer[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(
    inputBuffer[0].Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC);
249 250 251 252 253 254 255 256 257 258 259 260 261 262
  inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar = chr;
  inputBuffer[0].Event.KeyEvent.dwControlKeyState = 0;
  if ((HIBYTE(keyCode) & 1) == 1) {
    inputBuffer[0].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
  }
  if ((HIBYTE(keyCode) & 2) == 2) {
    inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED;
  }
  if ((HIBYTE(keyCode) & 4) == 4) {
    inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED;
  }
  inputBuffer[1].EventType = inputBuffer[0].EventType;
  inputBuffer[1].Event.KeyEvent.bKeyDown = FALSE;
  inputBuffer[1].Event.KeyEvent.wRepeatCount = 1;
263 264 265 266 267 268
  inputBuffer[1].Event.KeyEvent.wVirtualKeyCode =
    inputBuffer[0].Event.KeyEvent.wVirtualKeyCode;
  inputBuffer[1].Event.KeyEvent.wVirtualScanCode =
    inputBuffer[0].Event.KeyEvent.wVirtualScanCode;
  inputBuffer[1].Event.KeyEvent.uChar.UnicodeChar =
    inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar;
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
  inputBuffer[1].Event.KeyEvent.dwControlKeyState = 0;
}

//----------------------------------------------------------------------------
static int testPipe()
{
  int didFail = 1;
  HANDLE inPipeRead = INVALID_HANDLE_VALUE;
  HANDLE inPipeWrite = INVALID_HANDLE_VALUE;
  HANDLE outPipeRead = INVALID_HANDLE_VALUE;
  HANDLE outPipeWrite = INVALID_HANDLE_VALUE;
  HANDLE errPipeRead = INVALID_HANDLE_VALUE;
  HANDLE errPipeWrite = INVALID_HANDLE_VALUE;
  UINT currentCodepage = GetConsoleCP();
  char buffer[200];
284
  char buffer2[200];
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
  try {
    if (!createPipe(&inPipeRead, &inPipeWrite) ||
        !createPipe(&outPipeRead, &outPipeWrite) ||
        !createPipe(&errPipeRead, &errPipeWrite)) {
      throw std::runtime_error("createFile failed!");
    }
    if (TestCodepage == CP_ACP) {
      TestCodepage = GetACP();
    }
    if (!SetConsoleCP(TestCodepage)) {
      throw std::runtime_error("SetConsoleCP failed!");
    }

    DWORD bytesWritten = 0;
    if (!WriteFile(inPipeWrite, encodedInputTestString.c_str(),
300 301 302
                   (DWORD)encodedInputTestString.size(), &bytesWritten,
                   NULL) ||
        bytesWritten == 0) {
303 304 305 306 307
      throw std::runtime_error("WriteFile failed!");
    }

    if (createProcess(inPipeRead, outPipeWrite, errPipeWrite)) {
      try {
308
        DWORD status;
309 310
        if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
            WAIT_OBJECT_0) {
311
          std::cerr.setf(std::ios::hex, std::ios::basefield);
312 313
          std::cerr << "WaitForSingleObject returned unexpected status 0x"
                    << status << std::endl;
314
          std::cerr.unsetf(std::ios::hex);
315 316 317
          throw std::runtime_error("WaitForSingleObject failed!");
        }
        DWORD bytesRead = 0;
318 319
        if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead, NULL) ||
            bytesRead == 0) {
320
          throw std::runtime_error("ReadFile#1 failed!");
321
        }
322
        buffer[bytesRead] = 0;
323 324 325 326 327
        if ((bytesRead <
               encodedTestString.size() + 1 + encodedInputTestString.size() &&
             !ReadFile(outPipeRead, buffer + bytesRead,
                       sizeof(buffer) - bytesRead, &bytesRead, NULL)) ||
            bytesRead == 0) {
328
          throw std::runtime_error("ReadFile#2 failed!");
329 330 331 332 333 334 335
        }
        if (memcmp(buffer, encodedTestString.c_str(),
                   encodedTestString.size()) == 0 &&
            memcmp(buffer + encodedTestString.size() + 1,
                   encodedInputTestString.c_str(),
                   encodedInputTestString.size()) == 0) {
          bytesRead = 0;
336 337 338
          if (!ReadFile(errPipeRead, buffer2, sizeof(buffer2), &bytesRead,
                        NULL) ||
              bytesRead == 0) {
339
            throw std::runtime_error("ReadFile#3 failed!");
340
          }
341 342 343 344 345 346
          buffer2[bytesRead] = 0;
          didFail =
            encodedTestString.compare(0, encodedTestString.npos, buffer2,
                                      encodedTestString.size()) == 0
            ? 0
            : 1;
347 348
        }
        if (didFail != 0) {
349 350 351 352 353 354 355 356 357
          std::cerr << "Pipe's output didn't match expected output!"
                    << std::endl;
          dumpBuffers<char>(encodedTestString.c_str(), buffer,
                            encodedTestString.size());
          dumpBuffers<char>(encodedInputTestString.c_str(),
                            buffer + encodedTestString.size() + 1,
                            encodedInputTestString.size());
          dumpBuffers<char>(encodedTestString.c_str(), buffer2,
                            encodedTestString.size());
358
        }
359
      } catch (const std::runtime_error& ex) {
360
        DWORD lastError = GetLastError();
361
        std::cerr << "In function testPipe, line " << __LINE__ << ": "
362
                  << ex.what() << std::endl;
363
        displayError(lastError);
364 365 366
      }
      finishProcess(didFail == 0);
    }
367
  } catch (const std::runtime_error& ex) {
368
    DWORD lastError = GetLastError();
369 370
    std::cerr << "In function testPipe, line " << __LINE__ << ": " << ex.what()
              << std::endl;
371
    displayError(lastError);
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
  }
  finishPipe(inPipeRead, inPipeWrite);
  finishPipe(outPipeRead, outPipeWrite);
  finishPipe(errPipeRead, errPipeWrite);
  SetConsoleCP(currentCodepage);
  return didFail;
}

//----------------------------------------------------------------------------
static int testFile()
{
  int didFail = 1;
  HANDLE inFile = INVALID_HANDLE_VALUE;
  HANDLE outFile = INVALID_HANDLE_VALUE;
  HANDLE errFile = INVALID_HANDLE_VALUE;
  try {
    if ((inFile = createFile(L"stdinFile.txt")) == INVALID_HANDLE_VALUE ||
        (outFile = createFile(L"stdoutFile.txt")) == INVALID_HANDLE_VALUE ||
        (errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) {
      throw std::runtime_error("createFile failed!");
    }
    DWORD bytesWritten = 0;
    char buffer[200];
395
    char buffer2[200];
396

397
    int length;
398 399 400
    if ((length =
           WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString, -1,
                               buffer, sizeof(buffer), NULL, NULL)) == 0) {
401 402 403
      throw std::runtime_error("WideCharToMultiByte failed!");
    }
    buffer[length - 1] = '\n';
404 405
    if (!WriteFile(inFile, buffer, length, &bytesWritten, NULL) ||
        bytesWritten == 0) {
406 407 408 409 410 411 412 413 414
      throw std::runtime_error("WriteFile failed!");
    }
    if (SetFilePointer(inFile, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
      throw std::runtime_error("SetFilePointer failed!");
    }

    if (createProcess(inFile, outFile, errFile)) {
      DWORD bytesRead = 0;
      try {
415
        DWORD status;
416 417
        if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
            WAIT_OBJECT_0) {
418
          std::cerr.setf(std::ios::hex, std::ios::basefield);
419 420
          std::cerr << "WaitForSingleObject returned unexpected status 0x"
                    << status << std::endl;
421
          std::cerr.unsetf(std::ios::hex);
422 423
          throw std::runtime_error("WaitForSingleObject failed!");
        }
424 425
        if (SetFilePointer(outFile, 0, 0, FILE_BEGIN) ==
            INVALID_SET_FILE_POINTER) {
426
          throw std::runtime_error("SetFilePointer#1 failed!");
427
        }
428 429
        if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, NULL) ||
            bytesRead == 0) {
430
          throw std::runtime_error("ReadFile#1 failed!");
431
        }
432
        buffer[bytesRead] = 0;
433 434 435 436
        if (memcmp(buffer, encodedTestString.c_str(),
                   encodedTestString.size()) == 0 &&
            memcmp(buffer + encodedTestString.size() + 1,
                   encodedInputTestString.c_str(),
437
                   encodedInputTestString.size()) == 0) {
438
          bytesRead = 0;
439 440
          if (SetFilePointer(errFile, 0, 0, FILE_BEGIN) ==
              INVALID_SET_FILE_POINTER) {
441
            throw std::runtime_error("SetFilePointer#2 failed!");
442
          }
443

444 445
          if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead, NULL) ||
              bytesRead == 0) {
446
            throw std::runtime_error("ReadFile#2 failed!");
447
          }
448 449 450 451 452 453
          buffer2[bytesRead] = 0;
          didFail =
            encodedTestString.compare(0, encodedTestString.npos, buffer2,
                                      encodedTestString.size()) == 0
            ? 0
            : 1;
454 455
        }
        if (didFail != 0) {
456 457 458 459 460 461
          std::cerr << "File's output didn't match expected output!"
                    << std::endl;
          dumpBuffers<char>(encodedTestString.c_str(), buffer,
                            encodedTestString.size());
          dumpBuffers<char>(encodedInputTestString.c_str(),
                            buffer + encodedTestString.size() + 1,
462
                            encodedInputTestString.size());
463 464
          dumpBuffers<char>(encodedTestString.c_str(), buffer2,
                            encodedTestString.size());
465
        }
466
      } catch (const std::runtime_error& ex) {
467
        DWORD lastError = GetLastError();
468
        std::cerr << "In function testFile, line " << __LINE__ << ": "
469
                  << ex.what() << std::endl;
470
        displayError(lastError);
471 472 473
      }
      finishProcess(didFail == 0);
    }
474
  } catch (const std::runtime_error& ex) {
475
    DWORD lastError = GetLastError();
476 477
    std::cerr << "In function testFile, line " << __LINE__ << ": " << ex.what()
              << std::endl;
478
    displayError(lastError);
479 480 481 482 483 484 485 486
  }
  finishFile(inFile);
  finishFile(outFile);
  finishFile(errFile);
  return didFail;
}

#ifndef _WIN32_WINNT_VISTA
487
#define _WIN32_WINNT_VISTA 0x0600
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
#endif

//----------------------------------------------------------------------------
static int testConsole()
{
  int didFail = 1;
  HANDLE parentIn = GetStdHandle(STD_INPUT_HANDLE);
  HANDLE parentOut = GetStdHandle(STD_OUTPUT_HANDLE);
  HANDLE parentErr = GetStdHandle(STD_ERROR_HANDLE);
  HANDLE hIn = parentIn;
  HANDLE hOut = parentOut;
  DWORD consoleMode;
  bool newConsole = false;
  bool forceNewConsole = false;
  bool restoreConsole = false;
  LPCWSTR TestFaceName = L"Lucida Console";
  const DWORD TestFontFamily = 0x00000036;
  const DWORD TestFontSize = 0x000c0000;
  HKEY hConsoleKey;
  WCHAR FaceName[200];
508
  FaceName[0] = 0;
509 510 511 512
  DWORD FaceNameSize = sizeof(FaceName);
  DWORD FontFamily = TestFontFamily;
  DWORD FontSize = TestFontSize;
#ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
513 514 515 516 517
#pragma warning(push)
#ifdef __INTEL_COMPILER
#pragma warning(disable : 1478)
#else
#pragma warning(disable : 4996)
518
#endif
519 520 521
#endif
  const bool isVistaOrGreater =
    LOBYTE(LOWORD(GetVersion())) >= HIBYTE(_WIN32_WINNT_VISTA);
522
#ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
523
#pragma warning(pop)
524 525 526 527 528 529 530 531 532 533 534 535 536 537
#endif
  if (!isVistaOrGreater) {
    if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ | KEY_WRITE,
                      &hConsoleKey) == ERROR_SUCCESS) {
      DWORD dwordSize = sizeof(DWORD);
      if (RegQueryValueExW(hConsoleKey, L"FontFamily", NULL, NULL,
                           (LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) {
        if (FontFamily != TestFontFamily) {
          RegQueryValueExW(hConsoleKey, L"FaceName", NULL, NULL,
                           (LPBYTE)FaceName, &FaceNameSize);
          RegQueryValueExW(hConsoleKey, L"FontSize", NULL, NULL,
                           (LPBYTE)&FontSize, &dwordSize);

          RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
538
                         (BYTE*)&TestFontFamily, sizeof(TestFontFamily));
539
          RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ,
540 541
                         (BYTE*)TestFaceName,
                         (DWORD)((wcslen(TestFaceName) + 1) * sizeof(WCHAR)));
542
          RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD,
543
                         (BYTE*)&TestFontSize, sizeof(TestFontSize));
544 545 546 547 548

          restoreConsole = true;
          forceNewConsole = true;
        }
      } else {
549
        std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl;
550 551 552
      }
      RegCloseKey(hConsoleKey);
    } else {
553 554
      std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!"
                << std::endl;
555 556 557 558 559 560
    }
  }
  if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) {
    // Not a real console, let's create new one.
    FreeConsole();
    if (!AllocConsole()) {
561
      std::cerr << "AllocConsole failed!" << std::endl;
562 563 564 565 566 567
      return didFail;
    }
    SECURITY_ATTRIBUTES securityAttributes;
    securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
    securityAttributes.bInheritHandle = TRUE;
    securityAttributes.lpSecurityDescriptor = NULL;
568 569 570
    hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE,
                      FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
                      OPEN_EXISTING, 0, NULL);
571 572 573 574 575
    if (hIn == INVALID_HANDLE_VALUE) {
      DWORD lastError = GetLastError();
      std::cerr << "CreateFile(CONIN$)" << std::endl;
      displayError(lastError);
    }
576 577 578
    hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
                       FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
                       OPEN_EXISTING, 0, NULL);
579 580 581 582 583
    if (hOut == INVALID_HANDLE_VALUE) {
      DWORD lastError = GetLastError();
      std::cerr << "CreateFile(CONOUT$)" << std::endl;
      displayError(lastError);
    }
584 585 586 587 588 589 590 591 592 593 594 595
    SetStdHandle(STD_INPUT_HANDLE, hIn);
    SetStdHandle(STD_OUTPUT_HANDLE, hOut);
    SetStdHandle(STD_ERROR_HANDLE, hOut);
    newConsole = true;
  }

#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
  if (isVistaOrGreater) {
    CONSOLE_FONT_INFOEX consoleFont;
    memset(&consoleFont, 0, sizeof(consoleFont));
    consoleFont.cbSize = sizeof(consoleFont);
    HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
596 597 598 599 600 601 602 603 604 605 606 607
    typedef BOOL(WINAPI * GetCurrentConsoleFontExFunc)(
      HANDLE hConsoleOutput, BOOL bMaximumWindow,
      PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
    typedef BOOL(WINAPI * SetCurrentConsoleFontExFunc)(
      HANDLE hConsoleOutput, BOOL bMaximumWindow,
      PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
    GetCurrentConsoleFontExFunc getConsoleFont =
      (GetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
                                                  "GetCurrentConsoleFontEx");
    SetCurrentConsoleFontExFunc setConsoleFont =
      (SetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
                                                  "SetCurrentConsoleFontEx");
608 609 610 611 612
    if (getConsoleFont(hOut, FALSE, &consoleFont)) {
      if (consoleFont.FontFamily != TestFontFamily) {
        consoleFont.FontFamily = TestFontFamily;
        wcscpy(consoleFont.FaceName, TestFaceName);
        if (!setConsoleFont(hOut, FALSE, &consoleFont)) {
613
          std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl;
614 615 616
        }
      }
    } else {
617
      std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl;
618 619 620
    }
  } else {
#endif
621 622 623
    if (restoreConsole &&
        RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_WRITE,
                      &hConsoleKey) == ERROR_SUCCESS) {
624
      RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
625
                     (BYTE*)&FontFamily, sizeof(FontFamily));
626
      if (FaceName[0] != 0) {
627 628
        RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, (BYTE*)FaceName,
                       FaceNameSize);
629 630 631
      } else {
        RegDeleteValueW(hConsoleKey, L"FaceName");
      }
632 633
      RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, (BYTE*)&FontSize,
                     sizeof(FontSize));
634 635 636 637 638 639 640 641
      RegCloseKey(hConsoleKey);
    }
#if _WIN32_WINNT >= _WIN32_WINNT_VISTA
  }
#endif

  if (createProcess(NULL, NULL, NULL)) {
    try {
642
      DWORD status;
643 644
      if ((status = WaitForSingleObject(beforeInputEvent, waitTimeout)) !=
          WAIT_OBJECT_0) {
645
        std::cerr.setf(std::ios::hex, std::ios::basefield);
646 647
        std::cerr << "WaitForSingleObject returned unexpected status 0x"
                  << status << std::endl;
648 649
        std::cerr.unsetf(std::ios::hex);
        throw std::runtime_error("WaitForSingleObject#1 failed!");
650 651
      }
      INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) /
652 653
                                sizeof(UnicodeInputTestString[0])) *
                               2];
654
      memset(&inputBuffer, 0, sizeof(inputBuffer));
655
      unsigned int i;
656
      for (i = 0; i < (sizeof(UnicodeInputTestString) /
657 658 659 660
                         sizeof(UnicodeInputTestString[0]) -
                       1);
           i++) {
        writeInputKeyEvent(&inputBuffer[i * 2], UnicodeInputTestString[i]);
661
      }
662
      writeInputKeyEvent(&inputBuffer[i * 2], VK_RETURN);
663
      DWORD eventsWritten = 0;
664 665
      // We need to wait a bit before writing to console so child process have
      // started waiting for input on stdin.
666
      Sleep(300);
667 668 669 670
      if (!WriteConsoleInputW(hIn, inputBuffer,
                              sizeof(inputBuffer) / sizeof(inputBuffer[0]),
                              &eventsWritten) ||
          eventsWritten == 0) {
671 672
        throw std::runtime_error("WriteConsoleInput failed!");
      }
673 674
      if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
          WAIT_OBJECT_0) {
675
        std::cerr.setf(std::ios::hex, std::ios::basefield);
676 677
        std::cerr << "WaitForSingleObject returned unexpected status 0x"
                  << status << std::endl;
678 679
        std::cerr.unsetf(std::ios::hex);
        throw std::runtime_error("WaitForSingleObject#2 failed!");
680 681 682 683 684 685 686 687 688 689
      }
      CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
      if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) {
        throw std::runtime_error("GetConsoleScreenBufferInfo failed!");
      }

      COORD coord;
      DWORD charsRead = 0;
      coord.X = 0;
      coord.Y = screenBufferInfo.dwCursorPosition.Y - 4;
690
      WCHAR* outputBuffer = new WCHAR[screenBufferInfo.dwSize.X * 4];
691
      if (!ReadConsoleOutputCharacterW(hOut, outputBuffer,
692 693 694
                                       screenBufferInfo.dwSize.X * 4, coord,
                                       &charsRead) ||
          charsRead == 0) {
695 696 697 698
        delete[] outputBuffer;
        throw std::runtime_error("ReadConsoleOutputCharacter failed!");
      }
      std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString);
699
      std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' ');
700 701
      std::wstring wideInputTestString =
        kwsys::Encoding::ToWide(encodedInputTestString);
702 703
      if (memcmp(outputBuffer, wideTestString.c_str(),
                 wideTestString.size() * sizeof(wchar_t)) == 0 &&
704
          memcmp(outputBuffer + screenBufferInfo.dwSize.X * 1,
705 706
                 wideTestString.c_str(),
                 wideTestString.size() * sizeof(wchar_t)) == 0 &&
707
          memcmp(outputBuffer + screenBufferInfo.dwSize.X * 2,
708 709
                 UnicodeInputTestString,
                 sizeof(UnicodeInputTestString) - sizeof(WCHAR)) == 0 &&
710
          memcmp(outputBuffer + screenBufferInfo.dwSize.X * 3,
711
                 wideInputTestString.c_str(),
712
                 (wideInputTestString.size() - 1) * sizeof(wchar_t)) == 0) {
713 714
        didFail = 0;
      } else {
715 716 717 718 719 720 721 722 723 724 725 726 727
        std::cerr << "Console's output didn't match expected output!"
                  << std::endl;
        dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer,
                             wideTestString.size());
        dumpBuffers<wchar_t>(wideTestString.c_str(),
                             outputBuffer + screenBufferInfo.dwSize.X * 1,
                             wideTestString.size());
        dumpBuffers<wchar_t>(
          UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2,
          (sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR));
        dumpBuffers<wchar_t>(wideInputTestString.c_str(),
                             outputBuffer + screenBufferInfo.dwSize.X * 3,
                             wideInputTestString.size() - 1);
728 729
      }
      delete[] outputBuffer;
730
    } catch (const std::runtime_error& ex) {
731
      DWORD lastError = GetLastError();
732
      std::cerr << "In function testConsole, line " << __LINE__ << ": "
733
                << ex.what() << std::endl;
734
      displayError(lastError);
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
    }
    finishProcess(didFail == 0);
  }
  if (newConsole) {
    SetStdHandle(STD_INPUT_HANDLE, parentIn);
    SetStdHandle(STD_OUTPUT_HANDLE, parentOut);
    SetStdHandle(STD_ERROR_HANDLE, parentErr);
    CloseHandle(hIn);
    CloseHandle(hOut);
    FreeConsole();
  }
  return didFail;
}

#endif

//----------------------------------------------------------------------------
752
int testConsoleBuf(int, char* [])
753 754 755 756
{
  int ret = 0;

#if defined(_WIN32)
757
  beforeInputEvent = CreateEventW(NULL,
758 759 760
                                  FALSE, // auto-reset event
                                  FALSE, // initial state is nonsignaled
                                  BeforeInputEventName); // object name
761 762 763 764 765 766 767 768
  if (!beforeInputEvent) {
    std::cerr << "CreateEvent#1 failed " << GetLastError() << std::endl;
    return 1;
  }

  afterOutputEvent = CreateEventW(NULL, FALSE, FALSE, AfterOutputEventName);
  if (!afterOutputEvent) {
    std::cerr << "CreateEvent#2 failed " << GetLastError() << std::endl;
769 770 771
    return 1;
  }

772 773 774 775 776
  encodedTestString = kwsys::Encoding::ToNarrow(std::wstring(
    UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1));
  encodedInputTestString = kwsys::Encoding::ToNarrow(
    std::wstring(UnicodeInputTestString,
                 sizeof(UnicodeInputTestString) / sizeof(wchar_t) - 1));
777 778 779 780 781 782
  encodedInputTestString += "\n";

  ret |= testPipe();
  ret |= testFile();
  ret |= testConsole();

783 784
  CloseHandle(beforeInputEvent);
  CloseHandle(afterOutputEvent);
785 786 787 788
#endif

  return ret;
}