terminal/src/host/ut_host/AttrRowTests.cpp

736 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 "globals.h"
#include "../buffer/out/textBuffer.hpp"
#include "input.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
namespace WEX
{
namespace TestExecution
{
template<>
class VerifyOutputTraits<TextAttributeRun>
{
public:
static WEX::Common::NoThrowString ToString(const TextAttributeRun& tar)
{
return WEX::Common::NoThrowString().Format(
L"Length:%d, attr:%s",
tar.GetLength(),
VerifyOutputTraits<TextAttribute>::ToString(tar.GetAttributes()).GetBuffer());
}
};
template<>
class VerifyCompareTraits<TextAttributeRun, TextAttributeRun>
{
public:
static bool AreEqual(const TextAttributeRun& expected, const TextAttributeRun& actual)
{
return expected.GetAttributes() == actual.GetAttributes() &&
expected.GetLength() == actual.GetLength();
}
static bool AreSame(const TextAttributeRun& expected, const TextAttributeRun& actual)
{
return &expected == &actual;
}
static bool IsLessThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
static bool IsGreaterThan(const TextAttributeRun&, const TextAttributeRun&) = delete;
static bool IsNull(const TextAttributeRun& object)
{
return object.GetAttributes().IsLegacy() && object.GetAttributes().GetLegacyAttributes() == 0 &&
object.GetLength() == 0;
}
};
}
}
class AttrRowTests
{
ATTR_ROW* pSingle;
ATTR_ROW* pChain;
short _sDefaultLength = 80;
short _sDefaultChainLength = 6;
short sChainSegLength;
short sChainLeftover;
short sChainSegmentsNeeded;
WORD __wDefaultAttr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
WORD __wDefaultChainAttr = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
TextAttribute _DefaultAttr = TextAttribute(__wDefaultAttr);
TextAttribute _DefaultChainAttr = TextAttribute(__wDefaultChainAttr);
TEST_CLASS(AttrRowTests);
TEST_METHOD_SETUP(MethodSetup)
{
pSingle = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
// Segment length is the expected length divided by the row length
// E.g. row of 80, 4 segments, 20 segment length each
sChainSegLength = _sDefaultLength / _sDefaultChainLength;
// Leftover is spaces that don't fit evenly
// E.g. row of 81, 4 segments, 1 leftover length segment
sChainLeftover = _sDefaultLength % _sDefaultChainLength;
// Start with the number of segments we expect
sChainSegmentsNeeded = _sDefaultChainLength;
// If we had a remainder, add one more segment
if (sChainLeftover)
{
sChainSegmentsNeeded++;
}
// Create the chain
pChain = new ATTR_ROW(_sDefaultLength, _DefaultAttr);
pChain->_list.resize(sChainSegmentsNeeded);
// Attach all chain segments that are even multiples of the row length
for (short iChain = 0; iChain < _sDefaultChainLength; iChain++)
{
TextAttributeRun* pRun = &pChain->_list[iChain];
pRun->SetAttributes(TextAttribute{ gsl::narrow_cast<WORD>(iChain) }); // Just use the chain position as the value
pRun->SetLength(sChainSegLength);
}
if (sChainLeftover > 0)
{
// If we had a leftover, then this chain is one longer than we expected (the default length)
// So use it as the index (because indices start at 0)
TextAttributeRun* pRun = &pChain->_list[_sDefaultChainLength];
pRun->SetAttributes(_DefaultChainAttr);
pRun->SetLength(sChainLeftover);
}
return true;
}
TEST_METHOD_CLEANUP(MethodCleanup)
{
delete pSingle;
delete pChain;
return true;
}
TEST_METHOD(TestInitialize)
{
// Properties needed for test
const WORD wAttr = FOREGROUND_RED | BACKGROUND_BLUE;
TextAttribute attr = TextAttribute(wAttr);
// Cases to test
ATTR_ROW* pTestItems[]{ pSingle, pChain };
// Loop cases
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
pUnderTest->Reset(attr);
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), attr);
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
}
}
// Routine Description:
// - Packs an array of words representing attributes into the more compact storage form used by the row.
// Arguments:
// - rgAttrs - Array of words representing the attribute associated with each character position in the row.
// - cRowLength - Length of preceding array.
// - outAttrRun - reference to unique_ptr that will contain packed attr run on success.
// Return Value:
// - Success if success. Buffer too small if row length is incorrect.
HRESULT PackAttrs(_In_reads_(cRowLength) const TextAttribute* const rgAttrs,
const size_t cRowLength,
_Inout_ std::unique_ptr<TextAttributeRun[]>& outAttrRun,
_Out_ size_t* const cOutAttrRun)
{
NTSTATUS status = STATUS_SUCCESS;
if (cRowLength == 0)
{
status = STATUS_BUFFER_TOO_SMALL;
}
if (NT_SUCCESS(status))
{
// first count up the deltas in the array
size_t cDeltas = 1;
const TextAttribute* pPrevAttr = &rgAttrs[0];
for (size_t i = 1; i < cRowLength; i++)
{
const TextAttribute* pCurAttr = &rgAttrs[i];
if (*pCurAttr != *pPrevAttr)
{
cDeltas++;
}
pPrevAttr = pCurAttr;
}
// This whole situation was too complicated with a one off holder for one row run
// new method:
// delete the old buffer
// make a new buffer, one run + one run for each change
// set the values for each run one run index at a time
std::unique_ptr<TextAttributeRun[]> attrRun = std::make_unique<TextAttributeRun[]>(cDeltas);
status = NT_TESTNULL(attrRun.get());
if (NT_SUCCESS(status))
{
TextAttributeRun* pCurrentRun = attrRun.get();
pCurrentRun->SetAttributes(rgAttrs[0]);
pCurrentRun->SetLength(1);
for (size_t i = 1; i < cRowLength; i++)
{
if (pCurrentRun->GetAttributes() == rgAttrs[i])
{
pCurrentRun->SetLength(pCurrentRun->GetLength() + 1);
}
else
{
pCurrentRun++;
pCurrentRun->SetAttributes(rgAttrs[i]);
pCurrentRun->SetLength(1);
}
}
attrRun.swap(outAttrRun);
*cOutAttrRun = cDeltas;
}
}
return HRESULT_FROM_NT(status);
}
NoThrowString LogRunElement(_In_ TextAttributeRun& run)
{
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength());
}
void LogChain(_In_ PCWSTR pwszPrefix,
std::vector<TextAttributeRun>& chain)
{
NoThrowString str(pwszPrefix);
if (chain.size() > 0)
{
str.Append(LogRunElement(chain[0]));
for (size_t i = 1; i < chain.size(); i++)
{
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
}
}
Log::Comment(str);
}
void DoTestInsertAttrRuns(UINT& uiStartPos, WORD& ch1, UINT& uiChar1Length, WORD& ch2, UINT& uiChar2Length)
{
Log::Comment(String().Format(L"StartPos: %d, Char1: %wc, Char1Length: %d, Char2: %wc, Char2Length: %d",
uiStartPos,
ch1,
uiChar1Length,
ch2,
uiChar2Length));
bool const fUseStr2 = (ch2 != L'0');
// Set up our "original row" that we are going to try to insert into.
// This will represent a 10 column run of R3->B5->G2 that we will use for all tests.
ATTR_ROW originalRow{ static_cast<UINT>(_sDefaultLength), _DefaultAttr };
originalRow._list.resize(3);
originalRow._cchRowWidth = 10;
originalRow._list[0].SetAttributes(TextAttribute{ 'R' });
originalRow._list[0].SetLength(3);
originalRow._list[1].SetAttributes(TextAttribute{ 'B' });
originalRow._list[1].SetLength(5);
originalRow._list[2].SetAttributes(TextAttribute{ 'G' });
originalRow._list[2].SetLength(2);
LogChain(L"Original: ", originalRow._list);
// Set up our "insertion run"
size_t cInsertRow = 1;
if (fUseStr2)
{
cInsertRow++;
}
std::vector<TextAttributeRun> insertRow;
insertRow.resize(cInsertRow);
insertRow[0].SetAttributes(TextAttribute{ ch1 });
insertRow[0].SetLength(uiChar1Length);
if (fUseStr2)
{
insertRow[1].SetAttributes(TextAttribute{ ch2 });
insertRow[1].SetLength(uiChar2Length);
}
LogChain(L"Insert: ", insertRow);
Log::Comment(NoThrowString().Format(L"At Index: %d", uiStartPos));
UINT uiTotalLength = uiChar1Length;
if (fUseStr2)
{
uiTotalLength += uiChar2Length;
}
VERIFY_IS_TRUE((uiStartPos + uiTotalLength) >= 1); // assert we won't underflow.
UINT const uiEndPos = uiStartPos + uiTotalLength - 1;
// Calculate our expected final/result run by unpacking original, laying our insertion on it at the index
// then using our pack function to repack it.
// This method is easy to understand and very reliable, but its performance is bad.
// The InsertAttrRuns method we test against below is hard to understand but very high performance in production.
// - 1. Unpack
std::vector<TextAttribute> unpackedOriginal = { originalRow.begin(), originalRow.end() };
// - 2. Overlay insertion
UINT uiInsertedCount = 0;
UINT uiInsertIndex = 0;
// --- Walk through the unpacked run from start to end....
for (UINT uiUnpackedIndex = uiStartPos; uiUnpackedIndex <= uiEndPos; uiUnpackedIndex++)
{
// Pull the item from the insert run to analyze.
TextAttributeRun run = insertRow[uiInsertIndex];
// Copy the attribute from the run into the unpacked array
unpackedOriginal[uiUnpackedIndex] = run.GetAttributes();
// Increment how many times we've copied this particular portion of the run
uiInsertedCount++;
// If we've now inserted enough of them to match the length, advance the insert index and reset the counter.
if (uiInsertedCount >= run.GetLength())
{
uiInsertIndex++;
uiInsertedCount = 0;
}
}
// - 3. Pack.
std::unique_ptr<TextAttributeRun[]> packedRun;
size_t cPackedRun = 0;
VERIFY_SUCCEEDED(PackAttrs(unpackedOriginal.data(), originalRow._cchRowWidth, packedRun, &cPackedRun));
// Now send parameters into InsertAttrRuns and get its opinion on the subject.
VERIFY_SUCCEEDED(originalRow.InsertAttrRuns({ insertRow.data(), insertRow.size() }, uiStartPos, uiEndPos, (UINT)originalRow._cchRowWidth));
// Compare and ensure that the expected and actual match.
VERIFY_ARE_EQUAL(cPackedRun, originalRow._list.size(), L"Ensure that number of array elements required for RLE are the same.");
std::vector<TextAttributeRun> packedRunExpected;
std::copy_n(packedRun.get(), cPackedRun, std::back_inserter(packedRunExpected));
LogChain(L"Expected: ", packedRunExpected);
LogChain(L"Actual: ", originalRow._list);
for (size_t testIndex = 0; testIndex < cPackedRun; testIndex++)
{
VERIFY_ARE_EQUAL(packedRun[testIndex], originalRow._list[testIndex]);
}
}
TEST_METHOD(TestInsertAttrRunsSingle)
{
UINT const uiTestRunLength = 10;
UINT uiStartPos = 0;
WORD ch1 = L'0';
UINT uiChar1Length = 0;
WORD ch2 = L'0';
UINT uiChar2Length = 0;
Log::Comment(L"Test inserting a single item of a variable length into the run.");
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
{
ch1 = rgch1Options[iCh1Option];
for (UINT iCh1Length = 1; iCh1Length <= uiTestRunLength; iCh1Length++)
{
uiChar1Length = iCh1Length;
// We can't try to insert a run that's longer than would fit.
// If the run is of length 10 and we're trying to insert a length of 10,
// we can only insert at position 0.
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
// And so on...
UINT const uiMaxPos = uiTestRunLength - uiChar1Length;
for (UINT iStartPos = 0; iStartPos < uiMaxPos; iStartPos++)
{
uiStartPos = iStartPos;
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
}
}
}
}
TEST_METHOD(TestInsertAttrRunsMultiple)
{
UINT const uiTestRunLength = 10;
UINT uiStartPos = 0;
WORD ch1 = L'0';
UINT uiChar1Length = 0;
WORD ch2 = L'0';
UINT uiChar2Length = 0;
Log::Comment(L"Test inserting a multiple item run with each piece having variable length into the existing run.");
WORD rgch1Options[] = { L'X', L'R', L'G', L'B' };
for (size_t iCh1Option = 0; iCh1Option < ARRAYSIZE(rgch1Options); iCh1Option++)
{
ch1 = rgch1Options[iCh1Option];
UINT const uiMaxCh1Length = uiTestRunLength - 1; // leave at least 1 space for the second piece of the insert run.
for (UINT iCh1Length = 1; iCh1Length <= uiMaxCh1Length; iCh1Length++)
{
uiChar1Length = iCh1Length;
WORD rgch2Options[] = { L'Y' };
for (size_t iCh2Option = 0; iCh2Option < ARRAYSIZE(rgch2Options); iCh2Option++)
{
ch2 = rgch2Options[iCh2Option];
// When choosing the length of the second item, it can't be bigger than the remaining space in the run
// when accounting for the length of the first piece chosen.
// For example if the total run length is 10 and the first piece chosen was 8 long,
// the second piece can only be 1 or 2 long.
UINT const uiMaxCh2Length = uiTestRunLength - uiMaxCh1Length;
for (UINT iCh2Length = 1; iCh2Length <= uiMaxCh2Length; iCh2Length++)
{
uiChar2Length = iCh2Length;
// We can't try to insert a run that's longer than would fit.
// If the run is of length 10 and we're trying to insert a total length of 10,
// we can only insert at position 0.
// For the run length of 10 and an insert length of 9, we can try positions 0 and 1.
// And so on...
UINT const uiMaxPos = uiTestRunLength - (uiChar1Length + uiChar2Length);
for (UINT iStartPos = 0; iStartPos <= uiMaxPos; iStartPos++)
{
uiStartPos = iStartPos;
DoTestInsertAttrRuns(uiStartPos, ch1, uiChar1Length, ch2, uiChar2Length);
}
}
}
}
}
}
TEST_METHOD(TestUnpackAttrs)
{
Log::Comment(L"Checking unpack of a single color for the entire length");
{
const std::vector<TextAttribute> attrs{ pSingle->begin(), pSingle->end() };
for (auto& attr : attrs)
{
VERIFY_ARE_EQUAL(attr, _DefaultAttr);
}
}
Log::Comment(L"Checking unpack of the multiple color chain");
const std::vector<TextAttribute> attrs{ pChain->begin(), pChain->end() };
short cChainRun = 0; // how long we've been looking at the current piece of the chain
short iChainSegIndex = 0; // which piece of the chain we should be on right now
for (auto& attr : attrs)
{
// by default the chain was assembled above to have the chain segment index be the attribute
TextAttribute MatchingAttr = TextAttribute(iChainSegIndex);
// However, if the index is greater than the expected chain length, a remainder piece was made with a default attribute
if (iChainSegIndex >= _sDefaultChainLength)
{
MatchingAttr = _DefaultChainAttr;
}
VERIFY_ARE_EQUAL(attr, MatchingAttr);
// Add to the chain run
cChainRun++;
// If the chain run is greater than the length the segments were specified to be
if (cChainRun >= sChainSegLength)
{
// reset to 0
cChainRun = 0;
// move to the next chain segment down the line
iChainSegIndex++;
}
}
}
TEST_METHOD(TestReverseIteratorWalkFromMiddle)
{
// GH #3409, walking backwards through color range runs out of bounds
// We're going to create an attribute row with assorted colors and varying lengths
// just like the row of text on the Ubuntu prompt line that triggered this bug being found.
// Then we're going to walk backwards through the iterator like a selection-expand-to-left
// operation and ensure we don't run off the bounds.
// walk the chain, from index, stepSize at a time
// ensure we don't crash
auto testWalk = [](ATTR_ROW* chain, size_t index, int stepSize) {
// move to starting index
auto iter = chain->cbegin();
iter += index;
// Now walk backwards in a loop until 0.
while (iter)
{
iter -= stepSize;
}
Log::Comment(L"We made it through without crashing!");
};
// take one step of size stepSize on the chain
// index is where we start from
// expectedAttribute is what we expect to read here
auto verifyStep = [](ATTR_ROW* chain, size_t index, int stepSize, TextAttribute expectedAttribute) {
// move to starting index
auto iter = chain->cbegin();
iter += index;
// Now step backwards
iter -= stepSize;
VERIFY_ARE_EQUAL(expectedAttribute, *iter);
};
Log::Comment(L"Reverse iterate through ubuntu prompt");
{
// Create attr row representing a buffer that's 121 wide.
auto chain = std::make_unique<ATTR_ROW>(121, _DefaultAttr);
// The repro case had 4 chain segments.
chain->_list.resize(4);
// The color 10 went for the first 18.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(18);
// Default color for the next 1
chain->_list[1].SetAttributes(TextAttribute());
chain->_list[1].SetLength(1);
// Color 12 for the next 29
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(29);
// Then default color to end the run
chain->_list[3].SetAttributes(TextAttribute());
chain->_list[3].SetLength(73);
// The sum of the lengths should be 121.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength + chain->_list[3]._cchLength);
auto index = chain->_list[0].GetLength();
auto stepSize = 1;
testWalk(chain.get(), index, stepSize);
}
Log::Comment(L"Reverse iterate across a text run in the chain");
{
// Create attr row representing a buffer that's 3 wide.
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
// The repro case had 3 chain segments.
chain->_list.resize(3);
// The color 10 went for the first 1.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(1);
// The color 11 for the next 1
chain->_list[1].SetAttributes(TextAttribute(0xB));
chain->_list[1].SetLength(1);
// Color 12 for the next 1
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(1);
// The sum of the lengths should be 3.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
// on 'ABC', step from B to A
auto index = 1;
auto stepSize = 1;
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
}
Log::Comment(L"Reverse iterate across two text runs in the chain");
{
// Create attr row representing a buffer that's 3 wide.
auto chain = std::make_unique<ATTR_ROW>(3, _DefaultAttr);
// The repro case had 3 chain segments.
chain->_list.resize(3);
// The color 10 went for the first 1.
chain->_list[0].SetAttributes(TextAttribute(0xA));
chain->_list[0].SetLength(1);
// The color 11 for the next 1
chain->_list[1].SetAttributes(TextAttribute(0xB));
chain->_list[1].SetLength(1);
// Color 12 for the next 1
chain->_list[2].SetAttributes(TextAttribute(0xC));
chain->_list[2].SetLength(1);
// The sum of the lengths should be 3.
VERIFY_ARE_EQUAL(chain->_cchRowWidth, chain->_list[0]._cchLength + chain->_list[1]._cchLength + chain->_list[2]._cchLength);
// on 'ABC', step from C to A
auto index = 2;
auto stepSize = 2;
verifyStep(chain.get(), index, stepSize, TextAttribute(0xA));
}
}
TEST_METHOD(TestSetAttrToEnd)
{
const WORD wTestAttr = FOREGROUND_BLUE | BACKGROUND_GREEN;
TextAttribute TestAttr = TextAttribute(wTestAttr);
Log::Comment(L"FIRST: Set index to > 0 to test making/modifying chains");
const short iTestIndex = 50;
VERIFY_IS_TRUE(iTestIndex >= 0 && iTestIndex < _sDefaultLength);
Log::Comment(L"SetAttrToEnd for single color applied to whole string.");
pSingle->SetAttrToEnd(iTestIndex, TestAttr);
// Was 1 (single), should now have 2 segments
VERIFY_ARE_EQUAL(pSingle->_list.size(), 2u);
VERIFY_ARE_EQUAL(pSingle->_list[0].GetAttributes(), _DefaultAttr);
VERIFY_ARE_EQUAL(pSingle->_list[0].GetLength(), (unsigned int)(_sDefaultLength - (_sDefaultLength - iTestIndex)));
VERIFY_ARE_EQUAL(pSingle->_list[1].GetAttributes(), TestAttr);
VERIFY_ARE_EQUAL(pSingle->_list[1].GetLength(), (unsigned int)(_sDefaultLength - iTestIndex));
Log::Comment(L"SetAttrToEnd for existing chain of multiple colors.");
pChain->SetAttrToEnd(iTestIndex, TestAttr);
// From 7 segments down to 5.
VERIFY_ARE_EQUAL(pChain->_list.size(), 5u);
// Verify chain colors and lengths
VERIFY_ARE_EQUAL(TextAttribute(0), pChain->_list[0].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[0].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(1), pChain->_list[1].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[1].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(2), pChain->_list[2].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[2].GetLength(), (unsigned int)13);
VERIFY_ARE_EQUAL(TextAttribute(3), pChain->_list[3].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[3].GetLength(), (unsigned int)11);
VERIFY_ARE_EQUAL(TestAttr, pChain->_list[4].GetAttributes());
VERIFY_ARE_EQUAL(pChain->_list[4].GetLength(), (unsigned int)30);
Log::Comment(L"SECOND: Set index to 0 to test replacing anything with a single");
ATTR_ROW* pTestItems[]{ pSingle, pChain };
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
pUnderTest->SetAttrToEnd(0, TestAttr);
// should be down to 1 attribute set from beginning to end of string
VERIFY_ARE_EQUAL(pUnderTest->_list.size(), 1u);
// singular pair should contain the color
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetAttributes(), TestAttr);
// and its length should be the length of the whole string
VERIFY_ARE_EQUAL(pUnderTest->_list[0].GetLength(), (unsigned int)_sDefaultLength);
}
}
TEST_METHOD(TestTotalLength)
{
ATTR_ROW* pTestItems[]{ pSingle, pChain };
for (UINT iIndex = 0; iIndex < ARRAYSIZE(pTestItems); iIndex++)
{
ATTR_ROW* pUnderTest = pTestItems[iIndex];
const size_t Result = pUnderTest->_cchRowWidth;
VERIFY_ARE_EQUAL((short)Result, _sDefaultLength);
}
}
TEST_METHOD(TestResize)
{
CommonState state;
state.PrepareGlobalFont();
state.PrepareGlobalScreenBuffer();
pSingle->Resize(240);
pChain->Resize(240);
pSingle->Resize(255);
pChain->Resize(255);
pSingle->Resize(255);
pChain->Resize(255);
pSingle->Resize(60);
pChain->Resize(60);
pSingle->Resize(60);
pChain->Resize(60);
VERIFY_THROWS_SPECIFIC(pSingle->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
VERIFY_THROWS_SPECIFIC(pChain->Resize(0), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
state.CleanupGlobalScreenBuffer();
state.CleanupGlobalFont();
}
};