winpty/misc/buffer-tests/HandleTests_Traditional.cc
Ryan Prichard 2bb1ca205d Improve RemoteWorker and Event ctors.
* Allow creating a RemoteWorker object without spawning a new worker.
   It can be used like so:

   {
       Worker w(Worker::DoNotSpawn);
       if (...) {
           w = ...;
       } else {
           w = ...;
       }
   }

 * Remove the weird RemoteWorker(std::string) ctor.  The computation of the
   worker name should be inside the ctor.

 * It now becomes possible to initialize a RemoteWorker using syntax like:
      Worker w({ true, 0 });
   However, these are still ambiguous:
      Worker w({})  // could be move-init from a default-init Worker
      Worker w({1}) // could be move-init from a Worker(SpawnParams{1})

 * Make the Event ctor explicit.  The SpawnParams ctor needs to be
   non-explicit so it can be used with the braced initializer list.
2015-10-23 23:26:59 -05:00

287 lines
9.2 KiB
C++
Executable File

#include <TestCommon.h>
// Verify that the child's open console handle set is as expected from having
// just attached to or spawned from a source worker.
// * The set of child handles should exactly match the set of inheritable
// source handles.
// * Every open child handle should be inheritable.
static void checkAttachHandleSet(Worker &child, Worker &source) {
auto cvec = child.scanForConsoleHandles();
auto cvecInherit = inheritableHandles(cvec);
auto svecInherit = inheritableHandles(source.scanForConsoleHandles());
auto hv = &handleValues;
if (hv(cvecInherit) == hv(svecInherit) && allInheritable(cvec)) {
return;
}
source.dumpConsoleHandles();
child.dumpConsoleHandles();
CHECK(false && "checkAttachHandleSet failed");
}
static void Test_NewConsole_Resets_Everything() {
// Test that creating a new console properly resets everything.
//
// Tests:
// * CreateProcess - CREATE_NEW_CONSOLE
// * AllocConsole
//
printTestName(__FUNCTION__);
Worker p;
// Open some handles to demonstrate the "clean slate" outcome.
auto orig = { p.getStdin(), p.getStdout(), p.getStderr() };
p.getStdin().dup(TRUE).setStdin();
p.newBuffer(TRUE).setStderr().dup(TRUE).setStdout().activate();
for (auto &h : orig) {
Handle::invent(h.value(), p).close();
}
auto checkClean = [](Worker &proc) {
proc.dumpConsoleHandles();
CHECK_EQ(proc.getStdin().uvalue(), 0x3u);
CHECK_EQ(proc.getStdout().uvalue(), 0x7u);
CHECK_EQ(proc.getStderr().uvalue(), 0xbu);
auto handles = proc.scanForConsoleHandles();
CHECK(handleValues(handles) == (std::vector<HANDLE> {
proc.getStdin().value(),
proc.getStdout().value(),
proc.getStderr().value(),
}));
for (auto &h : handles) {
CHECK(h.inheritable());
}
};
// A child with a new console is reset to a blank slate.
auto c = p.child({ true, CREATE_NEW_CONSOLE });
checkClean(c);
// Similarly, detaching and allocating a new console resets everything.
p.detach();
p.alloc();
checkClean(p);
}
static void Test_DetachedProcess() {
// A child with DETACHED_PROCESS has no console, and its standard handles
// are set to 0 by default.
printTestName(__FUNCTION__);
Worker p;
p.getStdin().dup(TRUE).setStdin();
p.getStdout().dup(TRUE).setStdout();
p.getStderr().dup(TRUE).setStderr();
auto c = p.child({ true, DETACHED_PROCESS });
CHECK(c.getStdin().uvalue() == 0);
CHECK(c.getStdout().uvalue() == 0);
CHECK(c.getStderr().uvalue() == 0);
CHECK(c.scanForConsoleHandles().empty());
CHECK(c.consoleWindow() == NULL);
// XXX: With bInheritHandles=TRUE and DETACHED_PROCESS, are the standard handles always reset?
// - There are multiple cases to consider. i.e. Omitting STARTF_USESTDHANDLES vs specifying it.
// XXX: What do GetConsoleCP and GetConsoleOutputCP do when no console is attached?
// Verify that we have a blank slate even with an implicit console
// creation.
auto c2 = c.child({ true });
auto c2h = c2.scanForConsoleHandles();
CHECK(handleValues(c2h) == (std::vector<HANDLE> {
c2.getStdin().value(),
c2.getStdout().value(),
c2.getStderr().value(),
}));
}
static void Test_Creation_bInheritHandles_Flag() {
// The bInheritHandles flags to CreateProcess has no effect on console
// handles.
// XXX: I think it *does* on Windows 8 and up.
printTestName(__FUNCTION__);
Worker p;
for (auto &h : (Handle[]){
p.getStdin(),
p.getStdout(),
p.getStderr(),
p.newBuffer(FALSE),
p.newBuffer(TRUE),
}) {
h.dup(FALSE);
h.dup(TRUE);
}
auto cY = p.child({ true });
auto cN = p.child({ false });
CHECK(handleValues(cY.scanForConsoleHandles()) ==
handleValues(cN.scanForConsoleHandles()));
}
static void Test_HandleAllocationOrder() {
// When a new handle is created, it always assumes the lowest unused value.
printTestName(__FUNCTION__);
Worker p;
auto h3 = p.getStdin();
auto h7 = p.getStdout();
auto hb = p.getStderr();
auto hf = h7.dup(TRUE);
auto h13 = h3.dup(TRUE);
auto h17 = hb.dup(TRUE);
CHECK(h3.uvalue() == 0x3);
CHECK(h7.uvalue() == 0x7);
CHECK(hb.uvalue() == 0xb);
CHECK(hf.uvalue() == 0xf);
CHECK(h13.uvalue() == 0x13);
CHECK(h17.uvalue() == 0x17);
hf.close();
h13.close();
h7.close();
h7 = h3.dup(TRUE);
hf = h3.dup(TRUE);
h13 = h3.dup(TRUE);
auto h1b = h3.dup(TRUE);
CHECK(h7.uvalue() == 0x7);
CHECK(hf.uvalue() == 0xf);
CHECK(h13.uvalue() == 0x13);
CHECK(h1b.uvalue() == 0x1b);
}
static void Test_InheritNothing() {
// It's possible for the standard handles to be non-inheritable.
//
// Avoid calling DuplicateHandle(h, FALSE), because it produces inheritable
// console handles on Windows 7.
printTestName(__FUNCTION__);
Worker p;
auto conin = p.openConin();
auto conout = p.openConout();
p.getStdin().close();
p.getStdout().close();
p.getStderr().close();
conin.setStdin();
conout.setStdout().dup().setStderr();
p.dumpConsoleHandles();
auto c = p.child({ true });
// The child has no open console handles.
CHECK(c.scanForConsoleHandles().empty());
c.dumpConsoleHandles();
// The standard handle values are inherited, even though they're invalid.
CHECK(c.getStdin().value() == p.getStdin().value());
CHECK(c.getStdout().value() == p.getStdout().value());
CHECK(c.getStderr().value() == p.getStderr().value());
// Verify a console is attached.
CHECK(c.openConin().value() != INVALID_HANDLE_VALUE);
CHECK(c.openConout().value() != INVALID_HANDLE_VALUE);
CHECK(c.newBuffer().value() != INVALID_HANDLE_VALUE);
}
// XXX: Does specifying a handle in STD_{...}_HANDLE or hStd{Input,...}
// influence whether it is inherited, in any situation?
static void Test_AttachConsole_And_CreateProcess_Inheritance() {
printTestName(__FUNCTION__);
Worker p;
Worker unrelated({ false, DETACHED_PROCESS });
auto conin = p.getStdin().dup(TRUE);
auto conout1 = p.getStdout().dup(TRUE);
auto conout2 = p.getStderr().dup(TRUE);
p.openConout(FALSE); // an extra handle for checkAttachHandleSet testing
p.openConout(TRUE); // an extra handle for checkAttachHandleSet testing
p.getStdin().close();
p.getStdout().close();
p.getStderr().close();
conin.setStdin();
conout1.setStdout();
conout2.setStderr();
auto c = p.child({ true });
auto c2 = c.child({ true });
c2.detach();
c2.attach(c);
unrelated.attach(p);
// The first child will have the same standard handles as the parent.
CHECK(c.getStdin().value() == p.getStdin().value());
CHECK(c.getStdout().value() == p.getStdout().value());
CHECK(c.getStderr().value() == p.getStderr().value());
// AttachConsole always sets the handles to (0x3, 0x7, 0xb) regardless of
// handle validity. In this case, c2 initially had non-default handles,
// and it attached to a process that has and also initially had
// non-default handles. Nevertheless, the new standard handles are the
// defaults.
for (auto proc : {&c2, &unrelated}) {
CHECK(proc->getStdin().uvalue() == 0x3);
CHECK(proc->getStdout().uvalue() == 0x7);
CHECK(proc->getStderr().uvalue() == 0xb);
}
// The set of inheritable console handles in the two children exactly match
// that of the parent.
checkAttachHandleSet(c, p);
checkAttachHandleSet(c2, p);
checkAttachHandleSet(unrelated, p);
}
// XXX: This isn't quite ideal. FreeConsole's behavior is different:
// - traditional: wipe out ConsoleHandleSet, leave std handles alone
// - modern: close handles opened on attachment if any, leave std handles alone
static void Test_Detach_Implicitly_Closes_Handles() {
// After detaching, calling GetHandleInformation fails on previous console
// handles. Prior to Windows 8, this property applied to all console
// handles; in Windows 8, it only applies to some handles, apparently.
printTestName(__FUNCTION__);
Worker p;
Handle orig1[] = {
p.getStdin(),
p.getStdout(),
p.getStderr(),
};
Handle orig2[] = {
p.getStdin().dup(TRUE),
p.getStdout().dup(TRUE),
p.getStderr().dup(TRUE),
p.openConin(TRUE),
p.openConout(TRUE),
};
p.detach();
// After detaching the console, these handles are closed.
for (auto h : orig1) {
CHECK(!h.tryFlags());
}
if (!isAtLeastWin8()) {
// Previously, these handles were also closed.
for (auto h : orig2) {
CHECK(!h.tryFlags());
}
} else {
// As of Windows 8, these handles aren't closed.
for (auto h : orig2) {
CHECK(h.inheritable());
}
}
}
void runTraditionalTests() {
Test_NewConsole_Resets_Everything();
Test_DetachedProcess();
Test_Creation_bInheritHandles_Flag();
Test_HandleAllocationOrder();
Test_InheritNothing();
Test_AttachConsole_And_CreateProcess_Inheritance();
Test_Detach_Implicitly_Closes_Handles();
}