652 lines
28 KiB
C++
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);
|
|
}
|
|
};
|