terminal/src/inc/test/CommonState.hpp
Dustin L. Howett 3acb3d510b
bodgy: inline FillRow in FillTextBuffer to work around DD-2413379 (#18971)
Something about FillRow tickles the compiler and causes it to ICE during
LTCG.

See DD-2413379 for more.
DevCom:
https://developercommunity.visualstudio.com/t/VS-2022-1714-p11-cannot-complete-a-bui/10864784
2025-05-27 17:56:27 -05:00

273 lines
10 KiB
C++

/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
Module Name:
- CommonState.hpp
Abstract:
- This represents common boilerplate state setup required for unit tests to run
Author(s):
- Michael Niksa (miniksa) 18-Jun-2014
- Paul Campbell (paulcam) 18-Jun-2014
Revision History:
- Transformed to header-only class so it can be included by multiple
unit testing projects in the codebase without a bunch of overhead.
--*/
#pragma once
#include "../host/globals.h"
#include "../host/inputReadHandleData.h"
#include "../interactivity/inc/ServiceLocator.hpp"
class CommonState
{
public:
static const til::CoordType s_csWindowWidth = 80;
static const til::CoordType s_csWindowHeight = 80;
static const til::CoordType s_csBufferWidth = 80;
static const til::CoordType s_csBufferHeight = 300;
CommonState() :
m_heap(GetProcessHeap()),
m_hrTextBufferInfo(E_FAIL),
m_pFontInfo{ L"Consolas", 0, 0, { 8, 12 }, 0 },
m_backupTextBufferInfo(),
m_readHandle(nullptr)
{
}
~CommonState()
{
m_heap = nullptr;
}
void InitEvents()
{
Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().hInputEvent.create(wil::EventOptions::ManualReset);
}
void PrepareReadHandle()
{
m_readHandle = std::make_unique<INPUT_READ_HANDLE_DATA>();
}
void CleanupReadHandle()
{
m_readHandle.reset(nullptr);
}
void PrepareGlobalFont(const til::size coordFontSize = { 8, 12 })
{
m_pFontInfo = { L"Consolas", 0, 0, coordFontSize, 0 };
}
void PrepareGlobalScreenBuffer(const til::CoordType viewWidth = s_csWindowWidth,
const til::CoordType viewHeight = s_csWindowHeight,
const til::CoordType bufferWidth = s_csBufferWidth,
const til::CoordType bufferHeight = s_csBufferHeight)
{
Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals();
CONSOLE_INFORMATION& gci = g.getConsoleInformation();
til::size coordWindowSize;
coordWindowSize.width = viewWidth;
coordWindowSize.height = viewHeight;
til::size coordScreenBufferSize;
coordScreenBufferSize.width = bufferWidth;
coordScreenBufferSize.height = bufferHeight;
UINT uiCursorSize = 12;
THROW_IF_FAILED(SCREEN_INFORMATION::CreateInstance(coordWindowSize,
m_pFontInfo,
coordScreenBufferSize,
TextAttribute{},
TextAttribute{ FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED },
uiCursorSize,
&gci.pCurrentScreenBuffer));
// If we have a renderer, we need to call EnablePainting to initialize
// the viewport. If not, we mark the text buffer as inactive so that it
// doesn't try to trigger a redraw on a nonexistent renderer.
if (g.pRender)
{
g.pRender->EnablePainting();
}
else
{
gci.pCurrentScreenBuffer->_textBuffer->SetAsActiveBuffer(false);
}
}
void CleanupGlobalScreenBuffer()
{
const CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
delete gci.pCurrentScreenBuffer;
}
void PrepareGlobalInputBuffer()
{
CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
gci.pInputBuffer = new InputBuffer();
}
void CleanupGlobalInputBuffer()
{
const CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
delete gci.pInputBuffer;
}
void PrepareCookedReadData(const std::wstring_view initialData = {})
{
CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
auto* readData = new COOKED_READ_DATA(gci.pInputBuffer,
m_readHandle.get(),
gci.GetActiveOutputBuffer(),
0,
nullptr,
0,
L"",
initialData,
nullptr);
gci.SetCookedReadData(readData);
}
void CleanupCookedReadData()
{
CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
delete &gci.CookedReadData();
gci.SetCookedReadData(nullptr);
}
void PrepareNewTextBufferInfo(const bool useDefaultAttributes = false,
const til::CoordType bufferWidth = s_csBufferWidth,
const til::CoordType bufferHeight = s_csBufferHeight)
{
Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals();
CONSOLE_INFORMATION& gci = g.getConsoleInformation();
til::size coordScreenBufferSize;
coordScreenBufferSize.width = bufferWidth;
coordScreenBufferSize.height = bufferHeight;
UINT uiCursorSize = 12;
auto initialAttributes = useDefaultAttributes ? TextAttribute{} :
TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY };
m_backupTextBufferInfo.swap(gci.pCurrentScreenBuffer->_textBuffer);
try
{
std::unique_ptr<TextBuffer> textBuffer = std::make_unique<TextBuffer>(coordScreenBufferSize,
initialAttributes,
uiCursorSize,
true,
g.pRender);
if (textBuffer.get() == nullptr)
{
m_hrTextBufferInfo = E_OUTOFMEMORY;
}
else
{
m_hrTextBufferInfo = S_OK;
}
gci.pCurrentScreenBuffer->_textBuffer.swap(textBuffer);
// If we have a renderer, we need to call EnablePainting to initialize
// the viewport. If not, we mark the text buffer as inactive so that it
// doesn't try to trigger a redraw on a nonexistent renderer.
if (g.pRender)
{
g.pRender->EnablePainting();
}
else
{
gci.pCurrentScreenBuffer->_textBuffer->SetAsActiveBuffer(false);
}
}
catch (...)
{
m_hrTextBufferInfo = wil::ResultFromCaughtException();
}
}
void CleanupNewTextBufferInfo()
{
CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
VERIFY_IS_TRUE(gci.HasActiveOutputBuffer());
gci.pCurrentScreenBuffer->_textBuffer.swap(m_backupTextBufferInfo);
}
void FillTextBuffer()
{
CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation();
// fill with some assorted text that doesn't consume the whole row
const til::CoordType cRowsToFill = 4;
VERIFY_IS_TRUE(gci.HasActiveOutputBuffer());
TextBuffer& textBuffer = gci.GetActiveOutputBuffer().GetTextBuffer();
for (til::CoordType iRow = 0; iRow < cRowsToFill; iRow++)
{
ROW& row = textBuffer.GetMutableRowByOffset(iRow);
// fill a row
// - Each row is populated with L"AB\u304bC\u304dDE "
// - 7 characters, 6 spaces. 13 total
// - The characters take up first 9 columns. (The wide glyphs take up 2 columns each)
// - か = \x304b, き = \x304d
uint16_t column = 0;
for (const auto& ch : std::wstring_view{ L"AB\u304bC\u304dDE " })
{
const uint16_t width = ch >= 0x80 ? 2 : 1;
row.ReplaceCharacters(column, width, { &ch, 1 });
column += width;
}
// A = bright red on dark gray
// This string starts at index 0
auto Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
row.SetAttrToEnd(0, Attr);
// BかC = dark gold on bright blue
// This string starts at index 1
Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
row.SetAttrToEnd(1, Attr);
// き = bright white on dark purple
// This string starts at index 5
Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE);
row.SetAttrToEnd(5, Attr);
// DE = black on dark green
// This string starts at index 7
Attr = TextAttribute(BACKGROUND_GREEN);
row.SetAttrToEnd(7, Attr);
// odd rows forced a wrap
row.SetWrapForced(iRow & 1);
}
textBuffer.GetCursor().SetYPosition(cRowsToFill);
}
[[nodiscard]] HRESULT GetTextBufferInfoInitResult()
{
return m_hrTextBufferInfo;
}
private:
HANDLE m_heap;
HRESULT m_hrTextBufferInfo;
FontInfo m_pFontInfo;
std::unique_ptr<TextBuffer> m_backupTextBufferInfo;
std::unique_ptr<INPUT_READ_HANDLE_DATA> m_readHandle;
};