Skip to content
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

Add just the test infrastructure bits from #4354 #4382

Merged
3 commits merged into from
Jan 29, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ namespace Microsoft::Terminal::Core

// fwdecl unittest classes
#ifdef UNIT_TESTING
class ConptyRoundtripTests;
namespace TerminalCoreUnitTests
{
class TerminalBufferTests;
class ConptyRoundtripTests;
};
#endif

class Microsoft::Terminal::Core::Terminal final :
Expand Down Expand Up @@ -252,6 +256,7 @@ class Microsoft::Terminal::Core::Terminal final :
#pragma endregion

#ifdef UNIT_TESTING
friend class ::ConptyRoundtripTests;
friend class TerminalCoreUnitTests::TerminalBufferTests;
friend class TerminalCoreUnitTests::ConptyRoundtripTests;
#endif
};
148 changes: 62 additions & 86 deletions src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class InputBuffer; // This for some reason needs to be fwd-decl'd

#include "../cascadia/TerminalCore/Terminal.hpp"

#include "TestUtils.h"

using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
Expand All @@ -40,8 +42,17 @@ using namespace Microsoft::Console::Types;

using namespace Microsoft::Terminal::Core;

class ConptyRoundtripTests
namespace TerminalCoreUnitTests
{
class TerminalBufferTests;
};
using namespace TerminalCoreUnitTests;

class TerminalCoreUnitTests::ConptyRoundtripTests final
{
static const SHORT TerminalViewWidth = 80;
static const SHORT TerminalViewHeight = 32;

TEST_CLASS(ConptyRoundtripTests);

TEST_CLASS_SETUP(ClassSetup)
Expand All @@ -50,7 +61,7 @@ class ConptyRoundtripTests

m_state->InitEvents();
m_state->PrepareGlobalFont();
m_state->PrepareGlobalScreenBuffer();
m_state->PrepareGlobalScreenBuffer(TerminalViewWidth, TerminalViewHeight, TerminalViewWidth, TerminalViewHeight);
m_state->PrepareGlobalInputBuffer();

return true;
Expand All @@ -71,18 +82,19 @@ class ConptyRoundtripTests
{
// STEP 1: Set up the Terminal
term = std::make_unique<Terminal>();
term->Create({ CommonState::s_csBufferWidth, CommonState::s_csBufferHeight }, 0, emptyRT);
term->Create({ TerminalViewWidth, TerminalViewHeight }, 100, emptyRT);

// STEP 2: Set up the Conpty

// Set up some sane defaults
auto& g = ServiceLocator::LocateGlobals();
auto& gci = g.getConsoleInformation();

gci.SetDefaultForegroundColor(INVALID_COLOR);
gci.SetDefaultBackgroundColor(INVALID_COLOR);
gci.SetFillAttribute(0x07); // DARK_WHITE on DARK_BLACK

m_state->PrepareNewTextBufferInfo(true);
m_state->PrepareNewTextBufferInfo(true, TerminalViewWidth, TerminalViewHeight);
auto& currentBuffer = gci.GetActiveOutputBuffer();
// Make sure a test hasn't left us in the alt buffer on accident
VERIFY_IS_FALSE(currentBuffer._IsAltBuffer());
Expand All @@ -106,6 +118,13 @@ class ConptyRoundtripTests
g.pRender->AddRenderEngine(_pVtRenderEngine.get());
gci.GetActiveOutputBuffer().SetTerminalConnection(_pVtRenderEngine.get());

_pConApi = std::make_unique<ConhostInternalGetSet>(gci);

// Manually set the console into conpty mode. We're not actually going
// to set up the pipes for conpty, but we want the console to behave
// like it would in conpty mode.
g.EnableConptyModeForTests();

expectedOutput.clear();

return true;
Expand Down Expand Up @@ -133,9 +152,15 @@ class ConptyRoundtripTests
private:
bool _writeCallback(const char* const pch, size_t const cch);
void _flushFirstFrame();
void _resizeConpty(const unsigned short sx, const unsigned short sy);
std::deque<std::string> expectedOutput;
std::unique_ptr<Microsoft::Console::Render::VtEngine> _pVtRenderEngine;
std::unique_ptr<CommonState> m_state;
std::unique_ptr<Microsoft::Console::VirtualTerminal::ConGetSet> _pConApi;

// Tests can set these variables how they link to configure the behavior of the test harness.
bool _checkConptyOutput{ true }; // If true, the test class will check that the output from conpty was expected
bool _logConpty{ false }; // If true, the test class will log all the output from conpty. Helpful for debugging.

DummyRenderTarget emptyRT;
std::unique_ptr<Terminal> term;
Expand All @@ -144,18 +169,27 @@ class ConptyRoundtripTests
bool ConptyRoundtripTests::_writeCallback(const char* const pch, size_t const cch)
{
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()));

std::string first = expectedOutput.front();
expectedOutput.pop_front();
if (_checkConptyOutput)
{
VERIFY_IS_GREATER_THAN(expectedOutput.size(),
static_cast<size_t>(0),
NoThrowString().Format(L"writing=\"%hs\", expecting %u strings", actualString.c_str(), expectedOutput.size()));

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

VERIFY_ARE_EQUAL(first.length(), cch);
VERIFY_ARE_EQUAL(first, actualString);
Log::Comment(NoThrowString().Format(L"Expected =\t\"%hs\"", first.c_str()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this print raw escapes, or escape them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we replace 0x1b with 0x241b?

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

VERIFY_ARE_EQUAL(first.length(), cch);
VERIFY_ARE_EQUAL(first, actualString);
}
else if (_logConpty)
{
Log::Comment(NoThrowString().Format(
L"Writing \"%hs\" to Terminal", actualString.c_str()));
}

// Write the string back to our Terminal
const auto converted = ConvertToW(CP_UTF8, actualString);
Expand All @@ -177,75 +211,17 @@ void ConptyRoundtripTests::_flushFirstFrame()
VERIFY_SUCCEEDED(renderer.PaintFrame());
}

// Function Description:
// - Helper function to validate that a number of characters in a row are all
// the same. Validates that the next end-start characters are all equal to the
// provided string. Will move the provided iterator as it validates. The
// caller should ensure that `iter` starts where they would like to validate.
// Arguments:
// - expectedChar: The character (or characters) we're expecting
// - iter: a iterator pointing to the cell we'd like to start validating at.
// - start: the first index in the range we'd like to validate
// - end: the last index in the range we'd like to validate
// Return Value:
// - <none>
void _verifySpanOfText(const wchar_t* const expectedChar,
TextBufferCellIterator& iter,
const int start,
const int end)
void ConptyRoundtripTests::_resizeConpty(const unsigned short sx,
const unsigned short sy)
{
for (int x = start; x < end; x++)
// Largely taken from implementation in PtySignalInputThread::_InputThread
if (DispatchCommon::s_ResizeWindow(*_pConApi, sx, sy))
{
SetVerifyOutput settings(VerifyOutputSettings::LogOnlyFailures);
if (iter->Chars() != expectedChar)
{
Log::Comment(NoThrowString().Format(L"character [%d] was mismatched", x));
}
VERIFY_ARE_EQUAL(expectedChar, (iter++)->Chars());
// Instead of going through the VtIo to suppress the resize repaint,
// just call the method directly on the renderer. This is implemented in
// VtIo::SuppressResizeRepaint
VERIFY_SUCCEEDED(_pVtRenderEngine->SuppressResizeRepaint());
}
Log::Comment(NoThrowString().Format(
L"Successfully validated %d characters were '%s'", end - start, expectedChar));
}

// Function Description:
// - Helper function to validate that the next characters pointed to by `iter`
// are the provided string. Will increment iter as it walks the provided
// string of characters. It will leave `iter` on the first character after the
// expectedString.
// Arguments:
// - expectedString: The characters we're expecting
// - iter: a iterator pointing to the cell we'd like to start validating at.
// Return Value:
// - <none>
void _verifyExpectedString(std::wstring_view expectedString,
TextBufferCellIterator& iter)
{
for (const auto wch : expectedString)
{
wchar_t buffer[]{ wch, L'\0' };
std::wstring_view view{ buffer, 1 };
VERIFY_IS_TRUE(iter, L"Ensure iterator is still valid");
VERIFY_ARE_EQUAL(view, (iter++)->Chars(), NoThrowString().Format(L"%s", view.data()));
}
}

// Function Description:
// - Helper function to validate that the next characters in the buffer at the
// given location are the provided string. Will return an iterator on the
// first character after the expectedString.
// Arguments:
// - tb: the buffer who's content we should check
// - expectedString: The characters we're expecting
// - pos: the starting position in the buffer to check the contents of
// Return Value:
// - an iterator on the first character after the expectedString.
TextBufferCellIterator _verifyExpectedString(const TextBuffer& tb,
std::wstring_view expectedString,
const COORD pos)
{
auto iter = tb.GetCellDataAt(pos);
_verifyExpectedString(expectedString, iter);
return iter;
}

void ConptyRoundtripTests::ConptyOutputTestCanary()
Expand Down Expand Up @@ -278,7 +254,7 @@ void ConptyRoundtripTests::SimpleWriteOutputTest()

VERIFY_SUCCEEDED(renderer.PaintFrame());

_verifyExpectedString(termTb, L"Hello World ", { 0, 0 });
TestUtils::VerifyExpectedString(termTb, L"Hello World ", { 0, 0 });
}

void ConptyRoundtripTests::WriteTwoLinesUsesNewline()
Expand All @@ -302,8 +278,8 @@ void ConptyRoundtripTests::WriteTwoLinesUsesNewline()
hostSm.ProcessString(L"BBB");

auto verifyData = [](TextBuffer& tb) {
_verifyExpectedString(tb, L"AAA", { 0, 0 });
_verifyExpectedString(tb, L"BBB", { 0, 1 });
TestUtils::VerifyExpectedString(tb, L"AAA", { 0, 0 });
TestUtils::VerifyExpectedString(tb, L"BBB", { 0, 1 });
};

verifyData(hostTb);
Expand Down Expand Up @@ -338,10 +314,10 @@ void ConptyRoundtripTests::WriteAFewSimpleLines()
hostSm.ProcessString(L"\n");
hostSm.ProcessString(L"CCC");
auto verifyData = [](TextBuffer& tb) {
_verifyExpectedString(tb, L"AAA", { 0, 0 });
_verifyExpectedString(tb, L"BBB", { 0, 1 });
_verifyExpectedString(tb, L" ", { 0, 2 });
_verifyExpectedString(tb, L"CCC", { 0, 3 });
TestUtils::VerifyExpectedString(tb, L"AAA", { 0, 0 });
TestUtils::VerifyExpectedString(tb, L"BBB", { 0, 1 });
TestUtils::VerifyExpectedString(tb, L" ", { 0, 2 });
TestUtils::VerifyExpectedString(tb, L"CCC", { 0, 3 });
};

verifyData(hostTb);
Expand Down
68 changes: 68 additions & 0 deletions src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "precomp.h"
#include <WexTestClass.h>

#include "../renderer/inc/DummyRenderTarget.hpp"
#include "../cascadia/TerminalCore/Terminal.hpp"
#include "MockTermSettings.h"
#include "consoletaeftemplates.hpp"
#include "TestUtils.h"

using namespace winrt::Microsoft::Terminal::Settings;
using namespace Microsoft::Terminal::Core;

using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;

namespace TerminalCoreUnitTests
{
class TerminalBufferTests;
};
using namespace TerminalCoreUnitTests;

class TerminalCoreUnitTests::TerminalBufferTests final
{
TEST_CLASS(TerminalBufferTests);

TEST_METHOD(TestSimpleBufferWriting);

TEST_METHOD_SETUP(MethodSetup)
{
// STEP 1: Set up the Terminal
term = std::make_unique<Terminal>();
term->Create({ 80, 32 }, 100, emptyRT);
return true;
}

TEST_METHOD_CLEANUP(MethodCleanup)
{
term = nullptr;
return true;
}

private:
DummyRenderTarget emptyRT;
std::unique_ptr<Terminal> term;
};

void TerminalBufferTests::TestSimpleBufferWriting()
{
auto& termTb = *term->_buffer;
auto& termSm = *term->_stateMachine;
const auto initialView = term->GetViewport();

VERIFY_ARE_EQUAL(0, initialView.Top());
VERIFY_ARE_EQUAL(32, initialView.BottomExclusive());

termSm.ProcessString(L"Hello World");

const auto secondView = term->GetViewport();

VERIFY_ARE_EQUAL(0, secondView.Top());
VERIFY_ARE_EQUAL(32, secondView.BottomExclusive());

TestUtils::VerifyExpectedString(termTb, L"Hello World", { 0, 0 });
}
Loading