Skip to content

Commit

Permalink
Fix an off-by-one that made us use EL instead of ECH (microsoft#4994)
Browse files Browse the repository at this point in the history
When we painted spaces up until the character right before the right
edge of the screen, we would erroneously use Erase in Line instead of
Erase Character due to an off-by-one.

Fixes microsoft#4727
  • Loading branch information
DHowett authored Mar 18, 2020
1 parent eca0c6b commit d392a48
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 6 deletions.
67 changes: 62 additions & 5 deletions src/host/ut_host/ConptyOutputTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class ConptyOutputTests
TEST_METHOD(SimpleWriteOutputTest);
TEST_METHOD(WriteTwoLinesUsesNewline);
TEST_METHOD(WriteAFewSimpleLines);
TEST_METHOD(InvalidateUntilOneBeforeEnd);

private:
bool _writeCallback(const char* const pch, size_t const cch);
Expand All @@ -124,19 +125,23 @@ class ConptyOutputTests

bool ConptyOutputTests::_writeCallback(const char* const pch, size_t const cch)
{
// Since rendering happens on a background thread that doesn't have the exception handler on it
// we need to rely on VERIFY's return codes instead of exceptions.
const WEX::TestExecution::DisableVerifyExceptions disableExceptionsScope;

std::string actualString = std::string(pch, cch);
VERIFY_IS_GREATER_THAN(expectedOutput.size(),
static_cast<size_t>(0),
NoThrowString().Format(L"writing=\"%hs\", expecting %u strings", actualString.c_str(), expectedOutput.size()));
RETURN_BOOL_IF_FALSE(VERIFY_IS_GREATER_THAN(expectedOutput.size(),
static_cast<size_t>(0),
NoThrowString().Format(L"writing=\"%hs\", expecting %u strings", actualString.c_str(), expectedOutput.size())));

std::string first = expectedOutput.front();
expectedOutput.pop_front();

Log::Comment(NoThrowString().Format(L"Expected =\t\"%hs\"", first.c_str()));
Log::Comment(NoThrowString().Format(L"Actual =\t\"%hs\"", actualString.c_str()));

VERIFY_ARE_EQUAL(first.length(), cch);
VERIFY_ARE_EQUAL(first, actualString);
RETURN_BOOL_IF_FALSE(VERIFY_ARE_EQUAL(first.length(), cch));
RETURN_BOOL_IF_FALSE(VERIFY_ARE_EQUAL(first, actualString));

return true;
}
Expand Down Expand Up @@ -314,3 +319,55 @@ void ConptyOutputTests::WriteAFewSimpleLines()

VERIFY_SUCCEEDED(renderer.PaintFrame());
}

void ConptyOutputTests::InvalidateUntilOneBeforeEnd()
{
Log::Comment(NoThrowString().Format(
L"Make sure we don't use EL and wipe out the last column of text"));
VERIFY_IS_NOT_NULL(_pVtRenderEngine.get());

auto& g = ServiceLocator::LocateGlobals();
auto& renderer = *g.pRender;
auto& gci = g.getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& sm = si.GetStateMachine();
auto& tb = si.GetTextBuffer();

_flushFirstFrame();

// Move the cursor to width-15, draw 15 characters
sm.ProcessString(L"\x1b[1;66H");
sm.ProcessString(L"ABCDEFGHIJKLMNO");

{
auto iter = tb.GetCellDataAt({ 78, 0 });
VERIFY_ARE_EQUAL(L"N", (iter++)->Chars());
VERIFY_ARE_EQUAL(L"O", (iter++)->Chars());
}

expectedOutput.push_back("\x1b[65C");
expectedOutput.push_back("ABCDEFGHIJKLMNO");
expectedOutput.push_back("\x1b[1;80H"); // we move the cursor to the end of the line after paint

VERIFY_SUCCEEDED(renderer.PaintFrame());

// overstrike the first with X and the middle 8 with spaces
sm.ProcessString(L"\x1b[1;66H");
// ABCDEFGHIJKLMNO
sm.ProcessString(L"X ");

{
auto iter = tb.GetCellDataAt({ 78, 0 });
VERIFY_ARE_EQUAL(L" ", (iter++)->Chars());
VERIFY_ARE_EQUAL(L"O", (iter++)->Chars());
}

expectedOutput.push_back("\x1b[1;66H");
expectedOutput.push_back("X"); // sequence optimizer should choose ECH here
expectedOutput.push_back("\x1b[13X");
expectedOutput.push_back("\x1b[13C");

expectedOutput.push_back("\x1b[?25h"); // we turn the cursor back on for good measure

VERIFY_SUCCEEDED(renderer.PaintFrame());
}
2 changes: 1 addition & 1 deletion src/renderer/vt/paint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ using namespace Microsoft::Console::Types;
// before we need to print new text.
_deferredCursorPos = { _lastText.X + sNumSpaces, _lastText.Y };

if (_deferredCursorPos.X < _lastViewport.RightInclusive())
if (_deferredCursorPos.X <= _lastViewport.RightInclusive())
{
RETURN_IF_FAILED(_EraseCharacter(sNumSpaces));
}
Expand Down

0 comments on commit d392a48

Please sign in to comment.