-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure that delayed EOL wrap is reset when necessary #14936
Changes from 6 commits
841a064
83a323d
bdac748
a811441
a94bf69
4eada04
a086b49
5c8c47e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -252,6 +252,8 @@ class ScreenBufferTests | |
TEST_METHOD(TestDeferredMainBufferResize); | ||
|
||
TEST_METHOD(RectangularAreaOperations); | ||
|
||
TEST_METHOD(DelayedWrapReset); | ||
}; | ||
|
||
void ScreenBufferTests::SingleAlternateBufferCreationTest() | ||
|
@@ -6266,48 +6268,55 @@ void ScreenBufferTests::CursorSaveRestore() | |
VERIFY_SUCCEEDED(si.SetViewportOrigin(true, til::point(0, 0), true)); | ||
|
||
Log::Comment(L"Restore after save."); | ||
// Set the cursor position, attributes, and character set. | ||
// Set the cursor position, delayed wrap, attributes, and character set. | ||
cursor.SetPosition({ 20, 10 }); | ||
cursor.DelayEOLWrap(); | ||
si.SetAttributes(colorAttrs); | ||
stateMachine.ProcessString(selectGraphicsChars); | ||
// Save state. | ||
stateMachine.ProcessString(saveCursor); | ||
// Reset the cursor position, attributes, and character set. | ||
// Reset the cursor position, delayed wrap, attributes, and character set. | ||
cursor.SetPosition({ 0, 0 }); | ||
si.SetAttributes(defaultAttrs); | ||
stateMachine.ProcessString(selectAsciiChars); | ||
// Restore state. | ||
stateMachine.ProcessString(restoreCursor); | ||
// Verify initial position, colors, and graphic character set. | ||
// Verify initial position, delayed wrap, colors, and graphic character set. | ||
VERIFY_ARE_EQUAL(til::point(20, 10), cursor.GetPosition()); | ||
VERIFY_IS_TRUE(cursor.IsDelayedEOLWrap()); | ||
cursor.ResetDelayEOLWrap(); | ||
VERIFY_ARE_EQUAL(colorAttrs, si.GetAttributes()); | ||
stateMachine.ProcessString(asciiText); | ||
VERIFY_IS_TRUE(_ValidateLineContains({ 20, 10 }, graphicText, colorAttrs)); | ||
|
||
Log::Comment(L"Restore again without save."); | ||
// Reset the cursor position, attributes, and character set. | ||
// Reset the cursor position, delayed wrap, attributes, and character set. | ||
cursor.SetPosition({ 0, 0 }); | ||
si.SetAttributes(defaultAttrs); | ||
stateMachine.ProcessString(selectAsciiChars); | ||
// Restore state. | ||
stateMachine.ProcessString(restoreCursor); | ||
// Verify initial saved position, colors, and graphic character set. | ||
// Verify initial saved position, delayed wrap, colors, and graphic character set. | ||
VERIFY_ARE_EQUAL(til::point(20, 10), cursor.GetPosition()); | ||
VERIFY_IS_TRUE(cursor.IsDelayedEOLWrap()); | ||
cursor.ResetDelayEOLWrap(); | ||
VERIFY_ARE_EQUAL(colorAttrs, si.GetAttributes()); | ||
stateMachine.ProcessString(asciiText); | ||
VERIFY_IS_TRUE(_ValidateLineContains({ 20, 10 }, graphicText, colorAttrs)); | ||
|
||
Log::Comment(L"Restore after reset."); | ||
// Soft reset. | ||
stateMachine.ProcessString(L"\x1b[!p"); | ||
// Set the cursor position, attributes, and character set. | ||
// Set the cursor position, delayed wrap, attributes, and character set. | ||
cursor.SetPosition({ 20, 10 }); | ||
cursor.DelayEOLWrap(); | ||
si.SetAttributes(colorAttrs); | ||
stateMachine.ProcessString(selectGraphicsChars); | ||
// Restore state. | ||
stateMachine.ProcessString(restoreCursor); | ||
// Verify home position, default attributes, and ascii character set. | ||
// Verify home position, no delayed wrap, default attributes, and ascii character set. | ||
VERIFY_ARE_EQUAL(til::point(0, 0), cursor.GetPosition()); | ||
VERIFY_IS_FALSE(cursor.IsDelayedEOLWrap()); | ||
VERIFY_ARE_EQUAL(defaultAttrs, si.GetAttributes()); | ||
stateMachine.ProcessString(asciiText); | ||
VERIFY_IS_TRUE(_ValidateLineContains(til::point(0, 0), asciiText, defaultAttrs)); | ||
|
@@ -7538,3 +7547,99 @@ void ScreenBufferTests::RectangularAreaOperations() | |
VERIFY_IS_TRUE(_ValidateLinesContain(targetArea.top, targetArea.bottom, bufferChars, bufferAttr)); | ||
VERIFY_IS_TRUE(_ValidateLinesContain(targetArea.right, targetArea.top, targetArea.bottom, bufferChar, bufferAttr)); | ||
} | ||
|
||
void ScreenBufferTests::DelayedWrapReset() | ||
{ | ||
BEGIN_TEST_METHOD_PROPERTIES() | ||
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method") | ||
TEST_METHOD_PROPERTY(L"Data:op", L"{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34}") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like a loop over There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I know this is horrible, and I did consider using a loop, but the The other advantage of this approach is that you get a separate test case for each op, so say there's a failure in three of the ops, you'll get three separate errors, and you know exactly what's broken. With a loop, the first failure would prevent the rest of the ops from running, which isn't as nice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I for one am a big fan of the "gross set of test properties that result in separate test runs". Pretty sure I do that all over RoundtripTests 😝 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I rather like this approach as well. I have it on my list to take the Reflow tests (which use a custom TAEF COM class to offer table-based testing where each test case gets its own name!) and make them "generic" so that other tests can rely on them instead of this, but at the end of the day it is still a way to make a hundred individual test cases in TAEF's eyes. I have significant beef with the kind of test Leonard is suggesting: TEST_METHOD(TestEverythingEverywhereAllAtOnce) {
for(auto i = 0; i < 100; ++i) {
VERIFY_FALSE(foo, "foo failed lol");
}
} which iteration failed? good luck figuring it out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, some form of table-based testing would have been nice here. Ideally my There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed #14950 with a nod to this test 😄 |
||
END_TEST_METHOD_PROPERTIES(); | ||
|
||
int opIndex; | ||
VERIFY_SUCCEEDED(TestData::TryGetValue(L"op", opIndex)); | ||
|
||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); | ||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); | ||
auto& stateMachine = si.GetStateMachine(); | ||
const auto width = si.GetTextBuffer().GetSize().Width(); | ||
const auto halfWidth = width / 2; | ||
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING); | ||
WI_SetFlag(si.OutputMode, DISABLE_NEWLINE_AUTO_RETURN); | ||
stateMachine.ProcessString(L"\033[?40h"); // Make sure DECCOLM is allowed | ||
|
||
const auto startRow = 5; | ||
const auto startCol = width - 1; | ||
|
||
// The operations below are all those that the DEC STD 070 reference has | ||
// documented as needing to reset the Last Column Flag (see page D-13). The | ||
// only controls that we haven't included are HT and SUB, because most DEC | ||
// terminals did *not* trigger a reset after executing those sequences, and | ||
// most modern terminals also seem to have agreed that that is the correct | ||
// approach to take. | ||
const struct | ||
{ | ||
std::wstring_view name; | ||
std::wstring_view sequence; | ||
til::point expectedPos = {}; | ||
bool absolutePos = false; | ||
} ops[] = { | ||
{ L"DECSTBM", L"\033[1;10r", { 0, 0 }, true }, | ||
{ L"DECSWL", L"\033#5" }, | ||
{ L"DECDWL", L"\033#6", { halfWidth - 1, startRow }, true }, | ||
{ L"DECDHL (top)", L"\033#3", { halfWidth - 1, startRow }, true }, | ||
{ L"DECDHL (bottom)", L"\033#4", { halfWidth - 1, startRow }, true }, | ||
{ L"DECCOLM set", L"\033[?3h", { 0, 0 }, true }, | ||
{ L"DECOM set", L"\033[?6h", { 0, 0 }, true }, | ||
{ L"DECCOLM set", L"\033[?3l", { 0, 0 }, true }, | ||
{ L"DECOM reset", L"\033[?6l", { 0, 0 }, true }, | ||
{ L"DECAWM reset", L"\033[?7l" }, | ||
{ L"CUU", L"\033[A", { 0, -1 } }, | ||
{ L"CUD", L"\033[B", { 0, 1 } }, | ||
{ L"CUF", L"\033[C" }, | ||
{ L"CUB", L"\033[D", { -1, 0 } }, | ||
{ L"CUP", L"\033[3;7H", { 6, 2 }, true }, | ||
{ L"HVP", L"\033[3;7f", { 6, 2 }, true }, | ||
{ L"BS", L"\b", { -1, 0 } }, | ||
{ L"LF", L"\n", { 0, 1 } }, | ||
{ L"VT", L"\v", { 0, 1 } }, | ||
{ L"FF", L"\f", { 0, 1 } }, | ||
{ L"CR", L"\r", { 0, startRow }, true }, | ||
{ L"IND", L"\033D", { 0, 1 } }, | ||
{ L"RI", L"\033M", { 0, -1 } }, | ||
{ L"NEL", L"\033E", { 0, startRow + 1 }, true }, | ||
{ L"ECH", L"\033[X" }, | ||
{ L"DCH", L"\033[P" }, | ||
{ L"ICH", L"\033[@" }, | ||
{ L"EL", L"\033[K" }, | ||
{ L"DECSEL", L"\033[?K" }, | ||
{ L"DL", L"\033[M", { 0, startRow }, true }, | ||
{ L"IL", L"\033[L", { 0, startRow }, true }, | ||
{ L"ED", L"\033[J" }, | ||
{ L"ED (all)", L"\033[2J" }, | ||
{ L"ED (scrollback)", L"\033[3J" }, | ||
{ L"DECSED", L"\033[?J" }, | ||
}; | ||
const auto& op = gsl::at(ops, opIndex); | ||
|
||
Log::Comment(L"Writing a character at the end of the line should set delayed EOL wrap"); | ||
const auto startPos = til::point{ startCol, startRow }; | ||
si.GetTextBuffer().GetCursor().SetPosition(startPos); | ||
stateMachine.ProcessCharacter(L'X'); | ||
{ | ||
auto& cursor = si.GetTextBuffer().GetCursor(); | ||
VERIFY_IS_TRUE(cursor.IsDelayedEOLWrap()); | ||
VERIFY_ARE_EQUAL(startPos, cursor.GetPosition()); | ||
} | ||
|
||
Log::Comment(NoThrowString().Format( | ||
L"Executing a %s control should reset delayed EOL wrap", | ||
op.name.data())); | ||
const auto expectedPos = op.absolutePos ? op.expectedPos : startPos + op.expectedPos; | ||
stateMachine.ProcessString(op.sequence); | ||
{ | ||
auto& cursor = si.GetTextBuffer().GetCursor(); | ||
const auto actualPos = cursor.GetPosition() - si.GetViewport().Origin(); | ||
VERIFY_IS_FALSE(cursor.IsDelayedEOLWrap()); | ||
VERIFY_ARE_EQUAL(expectedPos, actualPos); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this. Removing parameters that should be known by internal state. 😄