541 lines
23 KiB
C++
541 lines
23 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 "../cmdline.h"
|
|
|
|
using namespace WEX::Common;
|
|
using namespace WEX::Logging;
|
|
using namespace WEX::TestExecution;
|
|
using Microsoft::Console::Interactivity::ServiceLocator;
|
|
|
|
constexpr size_t PROMPT_SIZE = 512;
|
|
|
|
class CommandLineTests
|
|
{
|
|
std::unique_ptr<CommonState> m_state;
|
|
CommandHistory* m_pHistory;
|
|
|
|
TEST_CLASS(CommandLineTests);
|
|
|
|
TEST_CLASS_SETUP(ClassSetup)
|
|
{
|
|
m_state = std::make_unique<CommonState>();
|
|
m_state->PrepareGlobalFont();
|
|
return true;
|
|
}
|
|
|
|
TEST_CLASS_CLEANUP(ClassCleanup)
|
|
{
|
|
m_state->CleanupGlobalFont();
|
|
return true;
|
|
}
|
|
|
|
TEST_METHOD_SETUP(MethodSetup)
|
|
{
|
|
m_state->PrepareGlobalScreenBuffer();
|
|
m_state->PrepareGlobalInputBuffer();
|
|
m_state->PrepareReadHandle();
|
|
m_state->PrepareCookedReadData();
|
|
m_pHistory = CommandHistory::s_Allocate(L"cmd.exe", (HANDLE)0);
|
|
if (!m_pHistory)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TEST_METHOD_CLEANUP(MethodCleanup)
|
|
{
|
|
CommandHistory::s_Free((HANDLE)0);
|
|
m_pHistory = nullptr;
|
|
m_state->CleanupCookedReadData();
|
|
m_state->CleanupReadHandle();
|
|
m_state->CleanupGlobalInputBuffer();
|
|
m_state->CleanupGlobalScreenBuffer();
|
|
return true;
|
|
}
|
|
|
|
void VerifyPromptText(COOKED_READ_DATA& cookedReadData, const std::wstring wstr)
|
|
{
|
|
const auto span = cookedReadData.SpanWholeBuffer();
|
|
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, wstr.size() * sizeof(wchar_t));
|
|
for (size_t i = 0; i < wstr.size(); ++i)
|
|
{
|
|
VERIFY_ARE_EQUAL(span.at(i), wstr.at(i));
|
|
}
|
|
}
|
|
|
|
void InitCookedReadData(COOKED_READ_DATA& cookedReadData,
|
|
CommandHistory* pHistory,
|
|
wchar_t* pBuffer,
|
|
const size_t cchBuffer)
|
|
{
|
|
cookedReadData._commandHistory = pHistory;
|
|
cookedReadData._userBuffer = pBuffer;
|
|
cookedReadData._userBufferSize = cchBuffer * sizeof(wchar_t);
|
|
cookedReadData._bufferSize = cchBuffer * sizeof(wchar_t);
|
|
cookedReadData._backupLimit = pBuffer;
|
|
cookedReadData._bufPtr = pBuffer;
|
|
cookedReadData._exeName = L"cmd.exe";
|
|
cookedReadData.OriginalCursorPosition() = { 0, 0 };
|
|
}
|
|
|
|
void SetPrompt(COOKED_READ_DATA& cookedReadData, const std::wstring text)
|
|
{
|
|
std::copy(text.begin(), text.end(), cookedReadData._backupLimit);
|
|
cookedReadData._bytesRead = text.size() * sizeof(wchar_t);
|
|
cookedReadData._currentPosition = text.size();
|
|
cookedReadData._bufPtr += text.size();
|
|
cookedReadData._visibleCharCount = text.size();
|
|
}
|
|
|
|
void MoveCursor(COOKED_READ_DATA& cookedReadData, const size_t column)
|
|
{
|
|
cookedReadData._currentPosition = column;
|
|
cookedReadData._bufPtr = cookedReadData._backupLimit + column;
|
|
}
|
|
|
|
TEST_METHOD(CanCycleCommandHistory)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
|
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 3", false));
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Next);
|
|
// should not have anything on the prompt
|
|
VERIFY_ARE_EQUAL(cookedReadData._bytesRead, 0u);
|
|
|
|
// go back one history item
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Previous);
|
|
VerifyPromptText(cookedReadData, L"echo 3");
|
|
|
|
// try to go to the next history item, prompt shouldn't change
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Next);
|
|
VerifyPromptText(cookedReadData, L"echo 3");
|
|
|
|
// go back another
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Previous);
|
|
VerifyPromptText(cookedReadData, L"echo 2");
|
|
|
|
// go forward
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Next);
|
|
VerifyPromptText(cookedReadData, L"echo 3");
|
|
|
|
// go back two
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Previous);
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Previous);
|
|
VerifyPromptText(cookedReadData, L"echo 1");
|
|
|
|
// make sure we can't go back further
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Previous);
|
|
VerifyPromptText(cookedReadData, L"echo 1");
|
|
|
|
// can still go forward
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Next);
|
|
VerifyPromptText(cookedReadData, L"echo 2");
|
|
}
|
|
|
|
TEST_METHOD(CanSetPromptToOldestHistory)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
|
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 3", false));
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._setPromptToOldestCommand(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"echo 1");
|
|
|
|
// change prompt and go back to oldest
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Next);
|
|
commandLine._setPromptToOldestCommand(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"echo 1");
|
|
}
|
|
|
|
TEST_METHOD(CanSetPromptToNewestHistory)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
|
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 3", false));
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._setPromptToNewestCommand(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"echo 3");
|
|
|
|
// change prompt and go back to newest
|
|
commandLine._processHistoryCycling(cookedReadData, CommandHistory::SearchDirection::Previous);
|
|
commandLine._setPromptToNewestCommand(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"echo 3");
|
|
}
|
|
|
|
TEST_METHOD(CanDeletePromptAfterCursor)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
auto expected = L"test word blah";
|
|
SetPrompt(cookedReadData, expected);
|
|
VerifyPromptText(cookedReadData, expected);
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
// set current cursor position somewhere in the middle of the prompt
|
|
MoveCursor(cookedReadData, 4);
|
|
commandLine.DeletePromptAfterCursor(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"test");
|
|
}
|
|
|
|
TEST_METHOD(CanDeletePromptBeforeCursor)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
auto expected = L"test word blah";
|
|
SetPrompt(cookedReadData, expected);
|
|
VerifyPromptText(cookedReadData, expected);
|
|
|
|
// set current cursor position somewhere in the middle of the prompt
|
|
MoveCursor(cookedReadData, 5);
|
|
auto& commandLine = CommandLine::Instance();
|
|
const COORD cursorPos = commandLine._deletePromptBeforeCursor(cookedReadData);
|
|
cookedReadData._currentPosition = cursorPos.X;
|
|
VerifyPromptText(cookedReadData, L"word blah");
|
|
}
|
|
|
|
TEST_METHOD(CanMoveCursorToEndOfPrompt)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
auto expected = L"test word blah";
|
|
SetPrompt(cookedReadData, expected);
|
|
VerifyPromptText(cookedReadData, expected);
|
|
|
|
// make sure the cursor is not at the start of the prompt
|
|
VERIFY_ARE_NOT_EQUAL(cookedReadData._currentPosition, 0u);
|
|
VERIFY_ARE_NOT_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
|
|
|
// save current position for later checking
|
|
const auto expectedCursorPos = cookedReadData._currentPosition;
|
|
const auto expectedBufferPos = cookedReadData._bufPtr;
|
|
|
|
MoveCursor(cookedReadData, 0);
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
const COORD cursorPos = commandLine._moveCursorToEndOfPrompt(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, gsl::narrow<short>(expectedCursorPos));
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, expectedCursorPos);
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, expectedBufferPos);
|
|
}
|
|
|
|
TEST_METHOD(CanMoveCursorToStartOfPrompt)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
auto expected = L"test word blah";
|
|
SetPrompt(cookedReadData, expected);
|
|
VerifyPromptText(cookedReadData, expected);
|
|
|
|
// make sure the cursor is not at the start of the prompt
|
|
VERIFY_ARE_NOT_EQUAL(cookedReadData._currentPosition, 0u);
|
|
VERIFY_ARE_NOT_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
const COORD cursorPos = commandLine._moveCursorToStartOfPrompt(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, 0);
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, 0u);
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
|
}
|
|
|
|
TEST_METHOD(CanMoveCursorLeftByWord)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
auto expected = L"test word blah";
|
|
SetPrompt(cookedReadData, expected);
|
|
VerifyPromptText(cookedReadData, expected);
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
// cursor position at beginning of "blah"
|
|
short expectedPos = 10;
|
|
COORD cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
|
|
|
// move again
|
|
expectedPos = 5; // before "word"
|
|
cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
|
|
|
// move again
|
|
expectedPos = 0; // before "test"
|
|
cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
|
|
|
// try to move again, nothing should happen
|
|
cursorPos = commandLine._moveCursorLeftByWord(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, gsl::narrow<size_t>(expectedPos));
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit + expectedPos);
|
|
}
|
|
|
|
TEST_METHOD(CanMoveCursorLeft)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
const std::wstring expected = L"test word blah";
|
|
SetPrompt(cookedReadData, expected);
|
|
VerifyPromptText(cookedReadData, expected);
|
|
|
|
// move left from end of prompt text to the beginning of the prompt
|
|
auto& commandLine = CommandLine::Instance();
|
|
for (auto it = expected.crbegin(); it != expected.crend(); ++it)
|
|
{
|
|
const COORD cursorPos = commandLine._moveCursorLeft(cookedReadData);
|
|
VERIFY_ARE_EQUAL(*cookedReadData._bufPtr, *it);
|
|
}
|
|
// should now be at the start of the prompt
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, 0u);
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
|
|
|
// try to move left a final time, nothing should change
|
|
const COORD cursorPos = commandLine._moveCursorLeft(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, 0);
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, 0u);
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, cookedReadData._backupLimit);
|
|
}
|
|
|
|
/*
|
|
// TODO MSFT:11285829 tcome back and turn these on once the system cursor isn't needed
|
|
TEST_METHOD(CanMoveCursorRightByWord)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
auto expected = L"test word blah";
|
|
SetPrompt(cookedReadData, expected);
|
|
VerifyPromptText(cookedReadData, expected);
|
|
|
|
// save current position for later checking
|
|
const auto endCursorPos = cookedReadData._currentPosition;
|
|
const auto endBufferPos = cookedReadData._bufPtr;
|
|
// NOTE: need to initialize the actually cursor and keep it up to date with the changes here. remove
|
|
once functions are fixed
|
|
// try to move right, nothing should happen
|
|
short expectedPos = gsl::narrow<short>(endCursorPos);
|
|
COORD cursorPos = MoveCursorRightByWord(cookedReadData);
|
|
VERIFY_ARE_EQUAL(cursorPos.X, expectedPos);
|
|
VERIFY_ARE_EQUAL(cookedReadData._currentPosition, expectedPos);
|
|
VERIFY_ARE_EQUAL(cookedReadData._bufPtr, endBufferPos);
|
|
|
|
// move to beginning of prompt and walk to the right by word
|
|
}
|
|
|
|
TEST_METHOD(CanMoveCursorRight)
|
|
{
|
|
}
|
|
|
|
TEST_METHOD(CanDeleteFromRightOfCursor)
|
|
{
|
|
}
|
|
|
|
*/
|
|
|
|
TEST_METHOD(CanInsertCtrlZ)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, nullptr, buffer.get(), PROMPT_SIZE);
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._insertCtrlZ(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"\x1a"); // ctrl-z
|
|
}
|
|
|
|
TEST_METHOD(CanDeleteCommandHistory)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
|
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 1", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 2", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"echo 3", false));
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._deleteCommandHistory(cookedReadData);
|
|
VERIFY_ARE_EQUAL(m_pHistory->GetNumberOfCommands(), 0u);
|
|
}
|
|
|
|
TEST_METHOD(CanFillPromptWithPreviousCommandFragment)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
|
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"I'm a little teapot", false));
|
|
SetPrompt(cookedReadData, L"short and stout");
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._fillPromptWithPreviousCommandFragment(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"short and stoutapot");
|
|
}
|
|
|
|
TEST_METHOD(CanCycleMatchingCommandHistory)
|
|
{
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
|
|
auto& cookedReadData = ServiceLocator::LocateGlobals().getConsoleInformation().CookedReadData();
|
|
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
|
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"I'm a little teapot", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"short and stout", false));
|
|
VERIFY_SUCCEEDED(m_pHistory->Add(L"inflammable", false));
|
|
|
|
SetPrompt(cookedReadData, L"i");
|
|
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"inflammable");
|
|
|
|
// make sure we skip to the next "i" history item
|
|
commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"I'm a little teapot");
|
|
|
|
// should cycle back to the start of the command history
|
|
commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData);
|
|
VerifyPromptText(cookedReadData, L"inflammable");
|
|
}
|
|
|
|
TEST_METHOD(CmdlineCtrlHomeFullwidthChars)
|
|
{
|
|
Log::Comment(L"Set up buffers, create cooked read data, get screen information.");
|
|
auto buffer = std::make_unique<wchar_t[]>(PROMPT_SIZE);
|
|
VERIFY_IS_NOT_NULL(buffer.get());
|
|
auto& consoleInfo = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
auto& screenInfo = consoleInfo.GetActiveOutputBuffer();
|
|
auto& cookedReadData = consoleInfo.CookedReadData();
|
|
InitCookedReadData(cookedReadData, m_pHistory, buffer.get(), PROMPT_SIZE);
|
|
|
|
Log::Comment(L"Create Japanese text string and calculate the distance we expect the cursor to move.");
|
|
const std::wstring text(L"\x30ab\x30ac\x30ad\x30ae\x30af"); // katakana KA GA KI GI KU
|
|
const auto bufferSize = screenInfo.GetBufferSize();
|
|
const auto cursorBefore = screenInfo.GetTextBuffer().GetCursor().GetPosition();
|
|
auto cursorAfterExpected = cursorBefore;
|
|
for (size_t i = 0; i < text.length() * 2; i++)
|
|
{
|
|
bufferSize.IncrementInBounds(cursorAfterExpected);
|
|
}
|
|
|
|
Log::Comment(L"Write the text into the buffer using the cooked read structures as if it came off of someone's input.");
|
|
const auto written = cookedReadData.Write(text);
|
|
VERIFY_ARE_EQUAL(text.length(), written);
|
|
|
|
Log::Comment(L"Retrieve the position of the cursor after insertion and check that it moved as far as we expected.");
|
|
const auto cursorAfter = screenInfo.GetTextBuffer().GetCursor().GetPosition();
|
|
VERIFY_ARE_EQUAL(cursorAfterExpected, cursorAfter);
|
|
|
|
Log::Comment(L"Walk through the screen buffer data and ensure that the text we wrote filled the cells up as we expected (2 cells per fullwidth char)");
|
|
{
|
|
auto cellIterator = screenInfo.GetCellDataAt(cursorBefore);
|
|
for (size_t i = 0; i < text.length() * 2; i++)
|
|
{
|
|
// Our original string was 5 wide characters which we expected to take 10 cells.
|
|
// Therefore each index of the original string will be used twice ( divide by 2 ).
|
|
const auto expectedTextValue = text.at(i / 2);
|
|
const String expectedText(&expectedTextValue, 1);
|
|
|
|
const auto actualTextValue = cellIterator->Chars();
|
|
const String actualText(actualTextValue.data(), gsl::narrow<int>(actualTextValue.size()));
|
|
|
|
VERIFY_ARE_EQUAL(expectedText, actualText);
|
|
cellIterator++;
|
|
}
|
|
}
|
|
|
|
Log::Comment(L"Now perform the command that is triggered with Ctrl+Home keys normally to erase the entire edit line.");
|
|
auto& commandLine = CommandLine::Instance();
|
|
commandLine._deletePromptBeforeCursor(cookedReadData);
|
|
|
|
Log::Comment(L"Check that the entire span of the buffer where we had the fullwidth text is now cleared out and full of blanks (nothing left behind).");
|
|
{
|
|
auto cursorPos = cursorBefore;
|
|
auto cellIterator = screenInfo.GetCellDataAt(cursorPos);
|
|
|
|
while (Utils::s_CompareCoords(cursorPos, cursorAfter) < 0)
|
|
{
|
|
const String expectedText(L"\x20"); // unicode space character
|
|
|
|
const auto actualTextValue = cellIterator->Chars();
|
|
const String actualText(actualTextValue.data(), gsl::narrow<int>(actualTextValue.size()));
|
|
|
|
VERIFY_ARE_EQUAL(expectedText, actualText);
|
|
cellIterator++;
|
|
|
|
bufferSize.IncrementInBounds(cursorPos);
|
|
}
|
|
}
|
|
}
|
|
};
|