terminal/src/host/ut_host/InputBufferTests.cpp

652 lines
28 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
#include "../../inc/consoletaeftemplates.hpp"
#include "CommonState.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/IInputEvent.hpp"
using namespace WEX::Logging;
using Microsoft::Console::Interactivity::ServiceLocator;
class InputBufferTests
{
TEST_CLASS(InputBufferTests);
std::unique_ptr<CommonState> m_state;
TEST_CLASS_SETUP(ClassSetup)
{
m_state = std::make_unique<CommonState>();
m_state->InitEvents();
return true;
}
TEST_CLASS_CLEANUP(ClassCleanup)
{
return true;
}
TEST_METHOD_CLEANUP(MethodCleanup)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
WI_ClearFlag(gci.Flags, CONSOLE_OUTPUT_SUSPENDED);
return true;
}
static const size_t RECORD_INSERT_COUNT = 12;
INPUT_RECORD MakeKeyEvent(BOOL bKeyDown,
WORD wRepeatCount,
WORD wVirtualKeyCode,
WORD wVirtualScanCode,
WCHAR UnicodeChar,
DWORD dwControlKeyState)
{
INPUT_RECORD retval;
retval.EventType = KEY_EVENT;
retval.Event.KeyEvent.bKeyDown = bKeyDown;
retval.Event.KeyEvent.wRepeatCount = wRepeatCount;
retval.Event.KeyEvent.wVirtualKeyCode = wVirtualKeyCode;
retval.Event.KeyEvent.wVirtualScanCode = wVirtualScanCode;
retval.Event.KeyEvent.uChar.UnicodeChar = UnicodeChar;
retval.Event.KeyEvent.dwControlKeyState = dwControlKeyState;
return retval;
}
TEST_METHOD(CanGetNumberOfReadyEvents)
{
InputBuffer inputBuffer;
INPUT_RECORD record = MakeKeyEvent(true, 1, L'a', 0, L'a', 0);
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u);
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u);
// add another event, check again
INPUT_RECORD record2;
record2.EventType = MENU_EVENT;
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record2)), 0u);
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 2u);
}
TEST_METHOD(CanInsertIntoInputBufferIndividually)
{
InputBuffer inputBuffer;
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
INPUT_RECORD record;
record.EventType = MENU_EVENT;
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u);
VERIFY_ARE_EQUAL(record, inputBuffer._storage.back()->ToInputRecord());
}
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT);
}
TEST_METHOD(CanBulkInsertIntoInputBuffer)
{
InputBuffer inputBuffer;
std::deque<std::unique_ptr<IInputEvent>> events;
INPUT_RECORD record;
record.EventType = MENU_EVENT;
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
events.push_back(IInputEvent::Create(record));
}
VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u);
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT);
// verify that the events are the same in storage
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_ARE_EQUAL(inputBuffer._storage[i]->ToInputRecord(), record);
}
}
TEST_METHOD(InputBufferCoalescesMouseEvents)
{
InputBuffer inputBuffer;
INPUT_RECORD mouseRecord;
mouseRecord.EventType = MOUSE_EVENT;
mouseRecord.Event.MouseEvent.dwEventFlags = MOUSE_MOVED;
// add a bunch of mouse event records
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
mouseRecord.Event.MouseEvent.dwMousePosition.X = static_cast<SHORT>(i + 1);
mouseRecord.Event.MouseEvent.dwMousePosition.Y = static_cast<SHORT>(i + 1) * 2;
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(mouseRecord)), 0u);
}
// check that they coalesced
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u);
// check that the mouse position is being updated correctly
const IInputEvent* const pOutEvent = inputBuffer._storage.front().get();
const MouseEvent* const pMouseEvent = static_cast<const MouseEvent* const>(pOutEvent);
VERIFY_ARE_EQUAL(pMouseEvent->GetPosition().X, static_cast<SHORT>(RECORD_INSERT_COUNT));
VERIFY_ARE_EQUAL(pMouseEvent->GetPosition().Y, static_cast<SHORT>(RECORD_INSERT_COUNT * 2));
// add a key event and another mouse event to make sure that
// an event between two mouse events stopped the coalescing.
INPUT_RECORD keyRecord;
keyRecord.EventType = KEY_EVENT;
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(keyRecord)), 0u);
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(mouseRecord)), 0u);
// verify
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 3u);
}
TEST_METHOD(InputBufferDoesNotCoalesceBulkMouseEvents)
{
Log::Comment(L"The input buffer should not coalesce mouse events if more than one event is sent at a time");
InputBuffer inputBuffer;
INPUT_RECORD mouseRecords[RECORD_INSERT_COUNT];
std::deque<std::unique_ptr<IInputEvent>> events;
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
mouseRecords[i].EventType = MOUSE_EVENT;
mouseRecords[i].Event.MouseEvent.dwEventFlags = MOUSE_MOVED;
events.push_back(IInputEvent::Create(mouseRecords[i]));
}
// add an extra event
events.push_front(IInputEvent::Create(mouseRecords[0]));
inputBuffer.Flush();
// send one mouse event to possibly coalesce into later
VERIFY_IS_GREATER_THAN(inputBuffer.Write(std::move(events.front())), 0u);
events.pop_front();
// write the others in bulk
VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u);
// no events should have been coalesced
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT + 1);
// check that the events stored match those inserted
VERIFY_ARE_EQUAL(inputBuffer._storage.front()->ToInputRecord(), mouseRecords[0]);
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_ARE_EQUAL(inputBuffer._storage[i + 1]->ToInputRecord(), mouseRecords[i]);
}
}
TEST_METHOD(InputBufferCoalescesKeyEvents)
{
Log::Comment(L"The input buffer should coalesce identical key events if they are send one at a time");
InputBuffer inputBuffer;
INPUT_RECORD record = MakeKeyEvent(true, 1, L'a', 0, L'a', 0);
// send a bunch of identical events
inputBuffer.Flush();
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u);
}
// all events should have been coalesced into one
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u);
// the single event should have a repeat count for each
// coalesced event
std::unique_ptr<IInputEvent> outEvent;
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvent,
true,
false,
false,
false));
VERIFY_ARE_NOT_EQUAL(nullptr, outEvent.get());
const KeyEvent* const pKeyEvent = static_cast<const KeyEvent* const>(outEvent.get());
VERIFY_ARE_EQUAL(pKeyEvent->GetRepeatCount(), RECORD_INSERT_COUNT);
}
TEST_METHOD(InputBufferDoesNotCoalesceBulkKeyEvents)
{
Log::Comment(L"The input buffer should not coalesce key events if more than one event is sent at a time");
InputBuffer inputBuffer;
INPUT_RECORD keyRecords[RECORD_INSERT_COUNT];
std::deque<std::unique_ptr<IInputEvent>> events;
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
keyRecords[i] = MakeKeyEvent(true, 1, L'a', 0, L'a', 0);
events.push_back(IInputEvent::Create(keyRecords[i]));
}
inputBuffer.Flush();
// send one key event to possibly coalesce into later
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(keyRecords[0])), 0u);
// write the others in bulk
VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u);
// no events should have been coalesced
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT + 1);
// check that the events stored match those inserted
VERIFY_ARE_EQUAL(inputBuffer._storage.front()->ToInputRecord(), keyRecords[0]);
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_ARE_EQUAL(inputBuffer._storage[i + 1]->ToInputRecord(), keyRecords[i]);
}
}
TEST_METHOD(InputBufferDoesNotCoalesceFullWidthChars)
{
InputBuffer inputBuffer;
WCHAR hiraganaA = 0x3042; // U+3042 hiragana A
INPUT_RECORD record = MakeKeyEvent(true, 1, hiraganaA, 0, hiraganaA, 0);
// send a bunch of identical events
inputBuffer.Flush();
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u);
VERIFY_ARE_EQUAL(inputBuffer._storage.back()->ToInputRecord(), record);
}
// The events shouldn't be coalesced
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT);
}
TEST_METHOD(CanFlushAllOutput)
{
InputBuffer inputBuffer;
std::deque<std::unique_ptr<IInputEvent>> events;
// put some events in the buffer so we can remove them
INPUT_RECORD record;
record.EventType = MENU_EVENT;
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
events.push_back(IInputEvent::Create(record));
}
VERIFY_IS_GREATER_THAN(inputBuffer.Write(events), 0u);
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT);
// remove them
inputBuffer.Flush();
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
}
TEST_METHOD(CanFlushAllButKeys)
{
InputBuffer inputBuffer;
INPUT_RECORD records[RECORD_INSERT_COUNT] = { 0 };
std::deque<std::unique_ptr<IInputEvent>> inEvents;
// create alternating mouse and key events
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
records[i].EventType = (i % 2 == 0) ? MENU_EVENT : KEY_EVENT;
inEvents.push_back(IInputEvent::Create(records[i]));
}
VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u);
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT);
// remove them
inputBuffer.FlushAllButKeys();
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT / 2);
// make sure that the non key events were the ones removed
std::deque<std::unique_ptr<IInputEvent>> outEvents;
size_t amountToRead = RECORD_INSERT_COUNT / 2;
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
amountToRead,
false,
false,
false,
false));
VERIFY_ARE_EQUAL(amountToRead, outEvents.size());
for (size_t i = 0; i < outEvents.size(); ++i)
{
VERIFY_ARE_EQUAL(outEvents[i]->EventType(), InputEventType::KeyEvent);
}
}
TEST_METHOD(CanReadInput)
{
InputBuffer inputBuffer;
INPUT_RECORD records[RECORD_INSERT_COUNT];
std::deque<std::unique_ptr<IInputEvent>> inEvents;
// write some input records
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
records[i] = MakeKeyEvent(TRUE, 1, static_cast<WCHAR>(L'A' + i), 0, static_cast<WCHAR>(L'A' + i), 0);
inEvents.push_back(IInputEvent::Create(records[i]));
}
VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u);
// read them back out
std::deque<std::unique_ptr<IInputEvent>> outEvents;
size_t amountToRead = RECORD_INSERT_COUNT;
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
amountToRead,
false,
false,
false,
false));
VERIFY_ARE_EQUAL(amountToRead, outEvents.size());
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
for (size_t i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_ARE_EQUAL(records[i], outEvents[i]->ToInputRecord());
}
}
TEST_METHOD(CanPeekAtEvents)
{
InputBuffer inputBuffer;
// add some events so that we have something to peek at
INPUT_RECORD records[RECORD_INSERT_COUNT];
std::deque<std::unique_ptr<IInputEvent>> inEvents;
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
records[i] = MakeKeyEvent(TRUE, 1, static_cast<WCHAR>(L'A' + i), 0, static_cast<WCHAR>(L'A' + i), 0);
inEvents.push_back(IInputEvent::Create(records[i]));
}
VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u);
// peek at events
std::deque<std::unique_ptr<IInputEvent>> outEvents;
size_t amountToRead = RECORD_INSERT_COUNT;
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
amountToRead,
true,
false,
false,
false));
VERIFY_ARE_EQUAL(amountToRead, outEvents.size());
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT);
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_ARE_EQUAL(records[i], outEvents[i]->ToInputRecord());
}
}
TEST_METHOD(EmptyingBufferDuringReadSetsResetWaitEvent)
{
Log::Comment(L"ResetWaitEvent should be true if a read to the buffer completely empties it");
InputBuffer inputBuffer;
// add some events so that we have something to stick in front of
INPUT_RECORD records[RECORD_INSERT_COUNT];
std::deque<std::unique_ptr<IInputEvent>> inEvents;
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
records[i] = MakeKeyEvent(TRUE, 1, static_cast<WCHAR>(L'A' + i), 0, static_cast<WCHAR>(L'A' + i), 0);
inEvents.push_back(IInputEvent::Create(records[i]));
}
VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u);
// read one record, make sure ResetWaitEvent isn't set
std::deque<std::unique_ptr<IInputEvent>> outEvents;
size_t eventsRead = 0;
bool resetWaitEvent = false;
inputBuffer._ReadBuffer(outEvents,
1,
eventsRead,
false,
resetWaitEvent,
true,
false);
VERIFY_ARE_EQUAL(eventsRead, 1u);
VERIFY_IS_FALSE(!!resetWaitEvent);
// read the rest, resetWaitEvent should be set to true
outEvents.clear();
inputBuffer._ReadBuffer(outEvents,
RECORD_INSERT_COUNT - 1,
eventsRead,
false,
resetWaitEvent,
true,
false);
VERIFY_ARE_EQUAL(eventsRead, RECORD_INSERT_COUNT - 1);
VERIFY_IS_TRUE(!!resetWaitEvent);
}
TEST_METHOD(ReadingDbcsCharsPadsOutputArray)
{
Log::Comment(L"During a non-unicode read, the input buffer should count twice for each dbcs key event");
// write a mouse event, key event, dbcs key event, mouse event
InputBuffer inputBuffer;
const unsigned int recordInsertCount = 4;
INPUT_RECORD inRecords[recordInsertCount];
inRecords[0].EventType = MOUSE_EVENT;
inRecords[1] = MakeKeyEvent(TRUE, 1, L'A', 0, L'A', 0);
inRecords[2] = MakeKeyEvent(TRUE, 1, 0x3042, 0, 0x3042, 0); // U+3042 hiragana A
inRecords[3].EventType = MOUSE_EVENT;
std::deque<std::unique_ptr<IInputEvent>> inEvents;
for (size_t i = 0; i < recordInsertCount; ++i)
{
inEvents.push_back(IInputEvent::Create(inRecords[i]));
}
inputBuffer.Flush();
VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u);
// read them out non-unicode style and compare
std::deque<std::unique_ptr<IInputEvent>> outEvents;
size_t eventsRead = 0;
bool resetWaitEvent = false;
inputBuffer._ReadBuffer(outEvents,
recordInsertCount,
eventsRead,
false,
resetWaitEvent,
false,
false);
// the dbcs record should have counted for two elements in
// the array, making it so that we get less events read
VERIFY_ARE_EQUAL(eventsRead, recordInsertCount - 1);
VERIFY_ARE_EQUAL(eventsRead, outEvents.size());
for (size_t i = 0; i < eventsRead; ++i)
{
VERIFY_ARE_EQUAL(outEvents[i]->ToInputRecord(), inRecords[i]);
}
}
TEST_METHOD(CanPrependEvents)
{
InputBuffer inputBuffer;
// add some events so that we have something to stick in front of
INPUT_RECORD records[RECORD_INSERT_COUNT];
std::deque<std::unique_ptr<IInputEvent>> inEvents;
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
records[i] = MakeKeyEvent(TRUE, 1, static_cast<WCHAR>(L'A' + i), 0, static_cast<WCHAR>(L'A' + i), 0);
inEvents.push_back(IInputEvent::Create(records[i]));
}
VERIFY_IS_GREATER_THAN(inputBuffer.Write(inEvents), 0u);
// prepend some other events
inEvents.clear();
INPUT_RECORD prependRecords[RECORD_INSERT_COUNT];
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
prependRecords[i] = MakeKeyEvent(TRUE, 1, static_cast<WCHAR>(L'a' + i), 0, static_cast<WCHAR>(L'a' + i), 0);
inEvents.push_back(IInputEvent::Create(prependRecords[i]));
}
size_t eventsWritten = inputBuffer.Prepend(inEvents);
VERIFY_ARE_EQUAL(eventsWritten, RECORD_INSERT_COUNT);
// grab the first set of events and ensure they match prependRecords
std::deque<std::unique_ptr<IInputEvent>> outEvents;
size_t amountToRead = RECORD_INSERT_COUNT;
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
amountToRead,
false,
false,
false,
false));
VERIFY_ARE_EQUAL(amountToRead, outEvents.size());
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), RECORD_INSERT_COUNT);
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_ARE_EQUAL(prependRecords[i], outEvents[i]->ToInputRecord());
}
outEvents.clear();
// verify the rest of the records
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
amountToRead,
false,
false,
false,
false));
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
VERIFY_ARE_EQUAL(amountToRead, outEvents.size());
for (unsigned int i = 0; i < RECORD_INSERT_COUNT; ++i)
{
VERIFY_ARE_EQUAL(records[i], outEvents[i]->ToInputRecord());
}
}
TEST_METHOD(CanReinitializeInputBuffer)
{
InputBuffer inputBuffer;
DWORD originalInputMode = inputBuffer.InputMode;
// change the buffer's state a bit
INPUT_RECORD record;
record.EventType = MENU_EVENT;
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(record)), 0u);
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u);
inputBuffer.InputMode = 0x0;
inputBuffer.ReinitializeInputBuffer();
// check that the changes were reverted
VERIFY_ARE_EQUAL(originalInputMode, inputBuffer.InputMode);
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
}
TEST_METHOD(HandleConsoleSuspensionEventsRemovesPauseKeys)
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
InputBuffer inputBuffer;
INPUT_RECORD pauseRecord = MakeKeyEvent(true, 1, VK_PAUSE, 0, 0, 0);
// make sure we aren't currently paused and have an empty buffer
VERIFY_IS_FALSE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED));
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(pauseRecord)), 0u);
// we should now be paused and the input record should be discarded
VERIFY_IS_TRUE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED));
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
// the next key press should unpause us but be discarded
INPUT_RECORD unpauseRecord = MakeKeyEvent(true, 1, L'a', 0, L'a', 0);
VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(unpauseRecord)), 0u);
VERIFY_IS_FALSE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED));
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
}
TEST_METHOD(SystemKeysDontUnpauseConsole)
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
InputBuffer inputBuffer;
INPUT_RECORD pauseRecord = MakeKeyEvent(true, 1, VK_PAUSE, 0, 0, 0);
// make sure we aren't currently paused and have an empty buffer
VERIFY_IS_FALSE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED));
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
// pause the screen
VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(pauseRecord)), 0u);
// we should now be paused and the input record should be discarded
VERIFY_IS_TRUE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED));
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 0u);
// sending a system key event should not stop the pause and
// the record should be stored in the input buffer
INPUT_RECORD systemRecord = MakeKeyEvent(true, 1, VK_CONTROL, 0, 0, 0);
VERIFY_IS_GREATER_THAN(inputBuffer.Write(IInputEvent::Create(systemRecord)), 0u);
VERIFY_IS_TRUE(WI_IsFlagSet(gci.Flags, CONSOLE_OUTPUT_SUSPENDED));
VERIFY_ARE_EQUAL(inputBuffer.GetNumberOfReadyEvents(), 1u);
std::deque<std::unique_ptr<IInputEvent>> outEvents;
size_t amountToRead = 2;
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
amountToRead,
true,
false,
false,
false));
}
TEST_METHOD(WritingToEmptyBufferSignalsWaitEvent)
{
InputBuffer inputBuffer;
INPUT_RECORD record = MakeKeyEvent(true, 1, L'a', 0, L'a', 0);
std::unique_ptr<IInputEvent> inputEvent = IInputEvent::Create(record);
size_t eventsWritten;
bool waitEvent = false;
inputBuffer.Flush();
// write one event to an empty buffer
std::deque<std::unique_ptr<IInputEvent>> storage;
storage.push_back(std::move(inputEvent));
inputBuffer._WriteBuffer(storage, eventsWritten, waitEvent);
VERIFY_IS_TRUE(waitEvent);
// write another, it shouldn't signal this time
INPUT_RECORD record2 = MakeKeyEvent(true, 1, L'b', 0, L'b', 0);
std::unique_ptr<IInputEvent> inputEvent2 = IInputEvent::Create(record2);
// write another event to a non-empty buffer
waitEvent = false;
storage.push_back(std::move(inputEvent2));
inputBuffer._WriteBuffer(storage, eventsWritten, waitEvent);
VERIFY_IS_FALSE(waitEvent);
}
TEST_METHOD(StreamReadingDeCoalesces)
{
InputBuffer inputBuffer;
const WORD repeatCount = 5;
INPUT_RECORD record = MakeKeyEvent(true, repeatCount, L'a', 0, L'a', 0);
std::deque<std::unique_ptr<IInputEvent>> outEvents;
VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(record)), 1u);
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
1,
false,
false,
true,
true));
VERIFY_ARE_EQUAL(outEvents.size(), 1u);
VERIFY_ARE_EQUAL(inputBuffer._storage.size(), 1u);
VERIFY_ARE_EQUAL(static_cast<const KeyEvent&>(*inputBuffer._storage.front()).GetRepeatCount(), repeatCount - 1);
VERIFY_ARE_EQUAL(static_cast<const KeyEvent&>(*outEvents.front()).GetRepeatCount(), 1u);
}
TEST_METHOD(StreamPeekingDeCoalesces)
{
InputBuffer inputBuffer;
const WORD repeatCount = 5;
INPUT_RECORD record = MakeKeyEvent(true, repeatCount, L'a', 0, L'a', 0);
std::deque<std::unique_ptr<IInputEvent>> outEvents;
VERIFY_ARE_EQUAL(inputBuffer.Write(IInputEvent::Create(record)), 1u);
VERIFY_SUCCESS_NTSTATUS(inputBuffer.Read(outEvents,
1,
true,
false,
true,
true));
VERIFY_ARE_EQUAL(outEvents.size(), 1u);
VERIFY_ARE_EQUAL(inputBuffer._storage.size(), 1u);
VERIFY_ARE_EQUAL(static_cast<const KeyEvent&>(*inputBuffer._storage.front()).GetRepeatCount(), repeatCount);
VERIFY_ARE_EQUAL(static_cast<const KeyEvent&>(*outEvents.front()).GetRepeatCount(), 1u);
}
};