installed pty
[VSoRC/.git] / node_modules / node-pty / deps / winpty / misc / ScreenBufferTest.cc
1 //
2 // Windows versions tested
3 //
4 // Vista Enterprise SP2 32-bit
5 //  - ver reports [Version 6.0.6002]
6 //  - kernel32.dll product/file versions are 6.0.6002.19381
7 //
8 // Windows 7 Ultimate SP1 32-bit
9 //  - ver reports [Version 6.1.7601]
10 //  - conhost.exe product/file versions are 6.1.7601.18847
11 //  - kernel32.dll product/file versions are 6.1.7601.18847
12 //
13 // Windows Server 2008 R2 Datacenter SP1 64-bit
14 //  - ver reports [Version 6.1.7601]
15 //  - conhost.exe product/file versions are 6.1.7601.23153
16 //  - kernel32.dll product/file versions are 6.1.7601.23153
17 //
18 // Windows 8 Enterprise 32-bit
19 //  - ver reports [Version 6.2.9200]
20 //  - conhost.exe product/file versions are 6.2.9200.16578
21 //  - kernel32.dll product/file versions are 6.2.9200.16859
22 //
23
24 //
25 // Specific version details on working Server 2008 R2:
26 //
27 //      dwMajorVersion      = 6
28 //      dwMinorVersion      = 1
29 //      dwBuildNumber       = 7601
30 //      dwPlatformId        = 2
31 //      szCSDVersion        = Service Pack 1
32 //      wServicePackMajor   = 1
33 //      wServicePackMinor   = 0
34 //      wSuiteMask          = 0x190
35 //      wProductType        = 0x3
36 //
37 // Specific version details on broken Win7:
38 //
39 //      dwMajorVersion      = 6
40 //      dwMinorVersion      = 1
41 //      dwBuildNumber       = 7601
42 //      dwPlatformId        = 2
43 //      szCSDVersion        = Service Pack 1
44 //      wServicePackMajor   = 1
45 //      wServicePackMinor   = 0
46 //      wSuiteMask          = 0x100
47 //      wProductType        = 0x1
48 //
49
50 #include <windows.h>
51 #include <stdio.h>
52 #include <string.h>
53
54 #include "TestUtil.cc"
55
56 const char *g_prefix = "";
57
58 static void dumpHandles() {
59     trace("%sSTDIN=0x%I64x STDOUT=0x%I64x STDERR=0x%I64x",
60         g_prefix,
61         (long long)GetStdHandle(STD_INPUT_HANDLE),
62         (long long)GetStdHandle(STD_OUTPUT_HANDLE),
63         (long long)GetStdHandle(STD_ERROR_HANDLE));
64 }
65
66 static const char *successOrFail(BOOL ret) {
67     return ret ? "ok" : "FAILED";
68 }
69
70 static void startChildInSameConsole(const wchar_t *args, BOOL
71                                     bInheritHandles=FALSE) {
72     wchar_t program[1024];
73     wchar_t cmdline[1024];
74     GetModuleFileNameW(NULL, program, 1024);
75     swprintf(cmdline, L"\"%ls\" %ls", program, args);
76
77     STARTUPINFOW sui;
78     PROCESS_INFORMATION pi;
79     memset(&sui, 0, sizeof(sui));
80     memset(&pi, 0, sizeof(pi));
81     sui.cb = sizeof(sui);
82
83     CreateProcessW(program, cmdline,
84                    NULL, NULL,
85                    /*bInheritHandles=*/bInheritHandles,
86                    /*dwCreationFlags=*/0,
87                    NULL, NULL,
88                    &sui, &pi);
89 }
90
91 static void closeHandle(HANDLE h) {
92     trace("%sClosing handle 0x%I64x...", g_prefix, (long long)h);
93     trace("%sClosing handle 0x%I64x... %s", g_prefix, (long long)h, successOrFail(CloseHandle(h)));
94 }
95
96 static HANDLE createBuffer() {
97
98     // If sa isn't provided, the handle defaults to not-inheritable.
99     SECURITY_ATTRIBUTES sa = {0};
100     sa.nLength = sizeof(sa);
101     sa.bInheritHandle = TRUE;
102
103     trace("%sCreating a new buffer...", g_prefix);
104     HANDLE conout = CreateConsoleScreenBuffer(
105                 GENERIC_READ | GENERIC_WRITE,
106                 FILE_SHARE_READ | FILE_SHARE_WRITE,
107                 &sa,
108                 CONSOLE_TEXTMODE_BUFFER, NULL);
109
110     trace("%sCreating a new buffer... 0x%I64x", g_prefix, (long long)conout);
111     return conout;
112 }
113
114 static HANDLE openConout() {
115
116     // If sa isn't provided, the handle defaults to not-inheritable.
117     SECURITY_ATTRIBUTES sa = {0};
118     sa.nLength = sizeof(sa);
119     sa.bInheritHandle = TRUE;
120
121     trace("%sOpening CONOUT...", g_prefix);
122     HANDLE conout = CreateFileW(L"CONOUT$",
123                 GENERIC_READ | GENERIC_WRITE,
124                 FILE_SHARE_READ | FILE_SHARE_WRITE,
125                 &sa,
126                 OPEN_EXISTING, 0, NULL);
127     trace("%sOpening CONOUT... 0x%I64x", g_prefix, (long long)conout);
128     return conout;
129 }
130
131 static void setConsoleActiveScreenBuffer(HANDLE conout) {
132     trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called...",
133         g_prefix, (long long)conout);
134     trace("%sSetConsoleActiveScreenBuffer(0x%I64x) called... %s",
135         g_prefix, (long long)conout,
136         successOrFail(SetConsoleActiveScreenBuffer(conout)));
137 }
138
139 static void writeTest(HANDLE conout, const char *msg) {
140     char writeData[256];
141     sprintf(writeData, "%s%s\n", g_prefix, msg);
142
143     trace("%sWriting to 0x%I64x: '%s'...",
144         g_prefix, (long long)conout, msg);
145     DWORD actual = 0;
146     BOOL ret = WriteConsoleA(conout, writeData, strlen(writeData), &actual, NULL);
147     trace("%sWriting to 0x%I64x: '%s'... %s",
148         g_prefix, (long long)conout, msg,
149         successOrFail(ret && actual == strlen(writeData)));
150 }
151
152 static void writeTest(const char *msg) {
153     writeTest(GetStdHandle(STD_OUTPUT_HANDLE), msg);
154 }
155
156
157
158 ///////////////////////////////////////////////////////////////////////////////
159 // TEST 1 -- create new buffer, activate it, and close the handle.  The console
160 // automatically switches the screen buffer back to the original.
161 //
162 // This test passes everywhere.
163 //
164
165 static void test1(int argc, char *argv[]) {
166     if (!strcmp(argv[1], "1")) {
167         startChildProcess(L"1:child");
168         return;
169     }
170
171     HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
172     writeTest(origBuffer, "<-- origBuffer -->");
173
174     HANDLE newBuffer = createBuffer();
175     writeTest(newBuffer, "<-- newBuffer -->");
176     setConsoleActiveScreenBuffer(newBuffer);
177     Sleep(2000);
178
179     writeTest(origBuffer, "TEST PASSED!");
180
181     // Closing the handle w/o switching the active screen buffer automatically
182     // switches the console back to the original buffer.
183     closeHandle(newBuffer);
184
185     while (true) {
186         Sleep(1000);
187     }
188 }
189
190
191
192 ///////////////////////////////////////////////////////////////////////////////
193 // TEST 2 -- Test program that creates and activates newBuffer, starts a child
194 // process, then closes its newBuffer handle.  newBuffer remains activated,
195 // because the child keeps it active.  (Also see TEST D.)
196 //
197
198 static void test2(int argc, char *argv[]) {
199     if (!strcmp(argv[1], "2")) {
200         startChildProcess(L"2:parent");
201         return;
202     }
203
204     if (!strcmp(argv[1], "2:parent")) {
205         g_prefix = "parent: ";
206         dumpHandles();
207         HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
208         writeTest(origBuffer, "<-- origBuffer -->");
209
210         HANDLE newBuffer = createBuffer();
211         writeTest(newBuffer, "<-- newBuffer -->");
212         setConsoleActiveScreenBuffer(newBuffer);
213
214         Sleep(1000);
215         writeTest(newBuffer, "bInheritHandles=FALSE:");
216         startChildInSameConsole(L"2:child", FALSE);
217         Sleep(1000);
218         writeTest(newBuffer, "bInheritHandles=TRUE:");
219         startChildInSameConsole(L"2:child", TRUE);
220
221         Sleep(1000);
222         trace("parent:----");
223
224         // Close the new buffer.  The active screen buffer doesn't automatically
225         // switch back to origBuffer, because the child process has a handle open
226         // to the original buffer.
227         closeHandle(newBuffer);
228
229         Sleep(600 * 1000);
230         return;
231     }
232
233     if (!strcmp(argv[1], "2:child")) {
234         g_prefix = "child: ";
235         dumpHandles();
236         // The child's output isn't visible, because it's still writing to
237         // origBuffer.
238         trace("child:----");
239         writeTest("writing to STDOUT");
240
241         // Handle inheritability is curious.  The console handles this program
242         // creates are inheritable, but CreateProcess is called with both
243         // bInheritHandles=TRUE and bInheritHandles=FALSE.
244         //
245         // Vista and Windows 7: bInheritHandles has no effect.  The child and
246         // parent processes have the same STDIN/STDOUT/STDERR handles:
247         // 0x3, 0x7, and 0xB.  The parent has a 0xF handle for newBuffer.
248         // The child can only write to 0x7, 0xB, and 0xF.  Only the writes to
249         // 0xF are visible (i.e. they touch newBuffer).
250         //
251         // Windows 8 or Windows 10 (legacy or non-legacy): the lowest 2 bits of
252         // the HANDLE to WriteConsole seem to be ignored.  The new process'
253         // console handles always refer to the buffer that was active when they
254         // started, but the values of the handles depend upon bInheritHandles.
255         // With bInheritHandles=TRUE, the child has the same
256         // STDIN/STDOUT/STDERR/newBuffer handles as the parent, and the three
257         // output handles all work, though their output is all visible.  With
258         // bInheritHandles=FALSE, the child has different STDIN/STDOUT/STDERR
259         // handles, and only the new STDOUT/STDERR handles work.
260         //
261         for (unsigned int i = 0x1; i <= 0xB0; ++i) {
262             char msg[256];
263             sprintf(msg, "Write to handle 0x%x", i);
264             HANDLE h = reinterpret_cast<HANDLE>(i);
265             writeTest(h, msg);
266         }
267
268         Sleep(600 * 1000);
269         return;
270     }
271 }
272
273
274
275 ///////////////////////////////////////////////////////////////////////////////
276 // TEST A -- demonstrate an apparent Windows bug with screen buffers
277 //
278 // Steps:
279 //  - The parent starts a child process.
280 //  - The child process creates and activates newBuffer
281 //  - The parent opens CONOUT$ and writes to it.
282 //  - The parent closes CONOUT$.
283 //     - At this point, broken Windows reactivates origBuffer.
284 //  - The child writes to newBuffer again.
285 //  - The child activates origBuffer again, then closes newBuffer.
286 //
287 // Test passes if the message "TEST PASSED!" is visible.
288 // Test commonly fails if conhost.exe crashes.
289 //
290 // Results:
291 //  - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
292 //  - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
293 //  - Windows 8 Enterprise 32-bit: PASS
294 //  - Windows 10 64-bit (legacy and non-legacy): PASS
295 //
296
297 static void testA_parentWork() {
298     // Open an extra CONOUT$ handle so that the HANDLE values in parent and
299     // child don't collide.  I think it's OK if they collide, but since we're
300     // trying to track down a Windows bug, it's best to avoid unnecessary
301     // complication.
302     HANDLE dummy = openConout();
303
304     Sleep(3000);
305
306     // Step 2: Open CONOUT$ in the parent.  This opens the active buffer, which
307     // was just created in the child.  It's handle 0x13.  Write to it.
308
309     HANDLE newBuffer = openConout();
310     writeTest(newBuffer, "step2: writing to newBuffer");
311
312     Sleep(3000);
313
314     // Step 3: Close handle 0x13.  With Windows 7, the console switches back to
315     // origBuffer, and (unless I'm missing something) it shouldn't.
316
317     closeHandle(newBuffer);
318 }
319
320 static void testA_childWork() {
321     HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
322
323     //
324     // Step 1: Create the new screen buffer in the child process and make it
325     // active.  (Typically, it's handle 0x0F.)
326     //
327
328     HANDLE newBuffer = createBuffer();
329
330     setConsoleActiveScreenBuffer(newBuffer);
331     writeTest(newBuffer, "<-- newBuffer -->");
332
333     Sleep(9000);
334     trace("child:----");
335
336     // Step 4: write to the newBuffer again.
337     writeTest(newBuffer, "TEST PASSED!");
338
339     //
340     // Step 5: Switch back to the original screen buffer and close the new
341     // buffer.  The switch call succeeds, but the CloseHandle call freezes for
342     // several seconds, because conhost.exe crashes.
343     //
344     Sleep(3000);
345
346     setConsoleActiveScreenBuffer(origBuffer);
347     writeTest(origBuffer, "writing to origBuffer");
348
349     closeHandle(newBuffer);
350
351     // The console HWND is NULL.
352     trace("child: console HWND=0x%I64x", (long long)GetConsoleWindow());
353
354     // At this point, the console window has closed, but the parent/child
355     // processes are still running.  Calling AllocConsole would fail, but
356     // calling FreeConsole followed by AllocConsole would both succeed, and a
357     // new console would appear.
358 }
359
360 static void testA(int argc, char *argv[]) {
361
362     if (!strcmp(argv[1], "A")) {
363         startChildProcess(L"A:parent");
364         return;
365     }
366
367     if (!strcmp(argv[1], "A:parent")) {
368         g_prefix = "parent: ";
369         trace("parent:----");
370         dumpHandles();
371         writeTest("<-- origBuffer -->");
372         startChildInSameConsole(L"A:child");
373         testA_parentWork();
374         Sleep(120000);
375         return;
376     }
377
378     if (!strcmp(argv[1], "A:child")) {
379         g_prefix = "child: ";
380         dumpHandles();
381         testA_childWork();
382         Sleep(120000);
383         return;
384     }
385 }
386
387
388
389 ///////////////////////////////////////////////////////////////////////////////
390 // TEST B -- invert TEST A -- also crashes conhost on Windows 7
391 //
392 // Test passes if the message "TEST PASSED!" is visible.
393 // Test commonly fails if conhost.exe crashes.
394 //
395 // Results:
396 //  - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
397 //  - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
398 //  - Windows 8 Enterprise 32-bit: PASS
399 //  - Windows 10 64-bit (legacy and non-legacy): PASS
400 //
401
402 static void testB(int argc, char *argv[]) {
403     if (!strcmp(argv[1], "B")) {
404         startChildProcess(L"B:parent");
405         return;
406     }
407
408     if (!strcmp(argv[1], "B:parent")) {
409         g_prefix = "parent: ";
410         startChildInSameConsole(L"B:child");
411         writeTest("<-- origBuffer -->");
412         HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
413
414         //
415         // Step 1: Create the new buffer and make it active.
416         //
417         trace("%s----", g_prefix);
418         HANDLE newBuffer = createBuffer();
419         setConsoleActiveScreenBuffer(newBuffer);
420         writeTest(newBuffer, "<-- newBuffer -->");
421
422         //
423         // Step 4: Attempt to write again to the new buffer.
424         //
425         Sleep(9000);
426         trace("%s----", g_prefix);
427         writeTest(newBuffer, "TEST PASSED!");
428
429         //
430         // Step 5: Switch back to the original buffer.
431         //
432         Sleep(3000);
433         trace("%s----", g_prefix);
434         setConsoleActiveScreenBuffer(origBuffer);
435         closeHandle(newBuffer);
436         writeTest(origBuffer, "writing to the initial buffer");
437
438         Sleep(60000);
439         return;
440     }
441
442     if (!strcmp(argv[1], "B:child")) {
443         g_prefix = "child: ";
444         Sleep(3000);
445         trace("%s----", g_prefix);
446
447         //
448         // Step 2: Open the newly active buffer and write to it.
449         //
450         HANDLE newBuffer = openConout();
451         writeTest(newBuffer, "writing to newBuffer");
452
453         //
454         // Step 3: Close the newly active buffer.
455         //
456         Sleep(3000);
457         closeHandle(newBuffer);
458
459         Sleep(60000);
460         return;
461     }
462 }
463
464
465
466 ///////////////////////////////////////////////////////////////////////////////
467 // TEST C -- Interleaving open/close of console handles also seems to break on
468 // Windows 7.
469 //
470 // Test:
471 //  - child creates and activates newBuf1
472 //  - parent opens newBuf1
473 //  - child creates and activates newBuf2
474 //  - parent opens newBuf2, then closes newBuf1
475 //  - child switches back to newBuf1
476 //     * At this point, the console starts malfunctioning.
477 //  - parent and child close newBuf2
478 //  - child closes newBuf1
479 //
480 // Test passes if the message "TEST PASSED!" is visible.
481 // Test commonly fails if conhost.exe crashes.
482 //
483 // Results:
484 //  - Windows 7 Ultimate SP1 32-bit: conhost.exe crashes
485 //  - Windows Server 2008 R2 Datacenter SP1 64-bit: PASS
486 //  - Windows 8 Enterprise 32-bit: PASS
487 //  - Windows 10 64-bit (legacy and non-legacy): PASS
488 //
489
490 static void testC(int argc, char *argv[]) {
491     if (!strcmp(argv[1], "C")) {
492         startChildProcess(L"C:parent");
493         return;
494     }
495
496     if (!strcmp(argv[1], "C:parent")) {
497         startChildInSameConsole(L"C:child");
498         writeTest("<-- origBuffer -->");
499         g_prefix = "parent: ";
500
501         // At time=4, open newBuffer1.
502         Sleep(4000);
503         trace("%s---- t=4", g_prefix);
504         const HANDLE newBuffer1 = openConout();
505
506         // At time=8, open newBuffer2, and close newBuffer1.
507         Sleep(4000);
508         trace("%s---- t=8", g_prefix);
509         const HANDLE newBuffer2 = openConout();
510         closeHandle(newBuffer1);
511
512         // At time=25, cleanup of newBuffer2.
513         Sleep(17000);
514         trace("%s---- t=25", g_prefix);
515         closeHandle(newBuffer2);
516
517         Sleep(240000);
518         return;
519     }
520
521     if (!strcmp(argv[1], "C:child")) {
522         g_prefix = "child: ";
523
524         // At time=2, create newBuffer1 and activate it.
525         Sleep(2000);
526         trace("%s---- t=2", g_prefix);
527         const HANDLE newBuffer1 = createBuffer();
528         setConsoleActiveScreenBuffer(newBuffer1);
529         writeTest(newBuffer1, "<-- newBuffer1 -->");
530
531         // At time=6, create newBuffer2 and activate it.
532         Sleep(4000);
533         trace("%s---- t=6", g_prefix);
534         const HANDLE newBuffer2 = createBuffer();
535         setConsoleActiveScreenBuffer(newBuffer2);
536         writeTest(newBuffer2, "<-- newBuffer2 -->");
537
538         // At time=10, attempt to switch back to newBuffer1.  The parent process
539         // has opened and closed its handle to newBuffer1, so does it still exist?
540         Sleep(4000);
541         trace("%s---- t=10", g_prefix);
542         setConsoleActiveScreenBuffer(newBuffer1);
543         writeTest(newBuffer1, "write to newBuffer1: TEST PASSED!");
544
545         // At time=25, cleanup of newBuffer2.
546         Sleep(15000);
547         trace("%s---- t=25", g_prefix);
548         closeHandle(newBuffer2);
549
550         // At time=35, cleanup of newBuffer1.  The console should switch to the
551         // initial buffer again.
552         Sleep(10000);
553         trace("%s---- t=35", g_prefix);
554         closeHandle(newBuffer1);
555
556         Sleep(240000);
557         return;
558     }
559 }
560
561
562
563 ///////////////////////////////////////////////////////////////////////////////
564 // TEST D -- parent creates a new buffer, child launches, writes,
565 // closes it output handle, then parent writes again.  (Also see TEST 2.)
566 //
567 // On success, this will appear:
568 //
569 //    parent: <-- newBuffer -->
570 //    child: writing to newBuffer
571 //    parent: TEST PASSED!
572 //
573 // If this appears, it indicates that the child's closing its output handle did
574 // not destroy newBuffer.
575 //
576 // Results:
577 //  - Windows 7 Ultimate SP1 32-bit: PASS
578 //  - Windows 8 Enterprise 32-bit: PASS
579 //  - Windows 10 64-bit (legacy and non-legacy): PASS
580 //
581
582 static void testD(int argc, char *argv[]) {
583     if (!strcmp(argv[1], "D")) {
584         startChildProcess(L"D:parent");
585         return;
586     }
587
588     if (!strcmp(argv[1], "D:parent")) {
589         g_prefix = "parent: ";
590         HANDLE origBuffer = GetStdHandle(STD_OUTPUT_HANDLE);
591         writeTest(origBuffer, "<-- origBuffer -->");
592
593         HANDLE newBuffer = createBuffer();
594         writeTest(newBuffer, "<-- newBuffer -->");
595         setConsoleActiveScreenBuffer(newBuffer);
596
597         // At t=2, start a child process, explicitly forcing it to use
598         // newBuffer for its standard handles.  These calls are apparently
599         // redundant on Windows 8 and up.
600         Sleep(2000);
601         trace("parent:----");
602         trace("parent: starting child process");
603         SetStdHandle(STD_OUTPUT_HANDLE, newBuffer);
604         SetStdHandle(STD_ERROR_HANDLE, newBuffer);
605         startChildInSameConsole(L"D:child");
606         SetStdHandle(STD_OUTPUT_HANDLE, origBuffer);
607         SetStdHandle(STD_ERROR_HANDLE, origBuffer);
608
609         // At t=6, write again to newBuffer.
610         Sleep(4000);
611         trace("parent:----");
612         writeTest(newBuffer, "TEST PASSED!");
613
614         // At t=8, close the newBuffer.  In earlier versions of windows
615         // (including Server 2008 R2), the console then switches back to
616         // origBuffer.  As of Windows 8, it doesn't, because somehow the child
617         // process is keeping the console on newBuffer, even though the child
618         // process closed its STDIN/STDOUT/STDERR handles.  Killing the child
619         // process by hand after the test finishes *does* force the console
620         // back to origBuffer.
621         Sleep(2000);
622         closeHandle(newBuffer);
623
624         Sleep(120000);
625         return;
626     }
627
628     if (!strcmp(argv[1], "D:child")) {
629         g_prefix = "child: ";
630         // At t=2, the child starts.
631         trace("child:----");
632         dumpHandles();
633         writeTest("writing to newBuffer");
634
635         // At t=4, the child explicitly closes its handle.
636         Sleep(2000);
637         trace("child:----");
638         if (GetStdHandle(STD_ERROR_HANDLE) != GetStdHandle(STD_OUTPUT_HANDLE)) {
639             closeHandle(GetStdHandle(STD_ERROR_HANDLE));
640         }
641         closeHandle(GetStdHandle(STD_OUTPUT_HANDLE));
642         closeHandle(GetStdHandle(STD_INPUT_HANDLE));
643
644         Sleep(120000);
645         return;
646     }
647 }
648
649
650
651 int main(int argc, char *argv[]) {
652     if (argc == 1) {
653         printf("USAGE: %s testnum\n", argv[0]);
654         return 0;
655     }
656
657     if (argv[1][0] == '1') {
658         test1(argc, argv);
659     } else if (argv[1][0] == '2') {
660         test2(argc, argv);
661     } else if (argv[1][0] == 'A') {
662         testA(argc, argv);
663     } else if (argv[1][0] == 'B') {
664         testB(argc, argv);
665     } else if (argv[1][0] == 'C') {
666         testC(argc, argv);
667     } else if (argv[1][0] == 'D') {
668         testD(argc, argv);
669     }
670     return 0;
671 }