terminal/src/host/ut_host/VtIoTests.cpp

494 lines
16 KiB
C++
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include <wextestclass.h>
#include "..\..\inc\consoletaeftemplates.hpp"
#include "..\..\types\inc\Viewport.hpp"
#include "..\..\renderer\vt\Xterm256Engine.hpp"
#include "..\..\renderer\vt\XtermEngine.hpp"
#include "..\..\renderer\base\Renderer.hpp"
#include "..\Settings.hpp"
#include "..\VtIo.hpp"
#ifndef __INSIDE_WINDOWS
#include "..\..\renderer\dx\DxRenderer.hpp"
#endif
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
using namespace std;
class Microsoft::Console::VirtualTerminal::VtIoTests
{
Move tests to invoke `te.exe` directly instead of using VSTest runner (#4490) Moves the tests from using the `vstest.console.exe` route to just using `te.exe`. PROs: - `te.exe` is significantly faster for running tests because the TAEF/VSTest adapter isn't great. - Running through `te.exe` is closer to what our developers are doing on their dev boxes - `te.exe` is how they run in the Windows gates. - `te.exe` doesn't seem to have the sporadic `0x6` error code thrown during the tests where somehow the console handles get lost - `te.exe` doesn't seem to repro the other intermittent issues that we have been having that are inscrutable. - Fewer processes in the tree (te is running anyway under `vstest.console.exe`, just indirected a lot - The log outputs scroll live with all our logging messages instead of suppressing everything until there's a failure - The log output is actually in the order things are happening versus vstest. CONs: - No more code coverage. - No more test records in the ADO build/test panel. - Tests really won't work inside Visual Studio at all. - The log files are really big now - Testing is not a test task anymore, just another script. Refuting each CON: - We didn't read the code coverage numbers - We didn't look at the ADO test panel results or build-over-build velocities - Tests didn't really work inside Visual Studio anyway unless you did the right incantations under the full moon. - We could tone down the logging if we wanted at either the te.exe execution time (with a switch) or by declaring properties in the tests/classes/modules that are very verbose to not log unless it fails. - I don't think anyone cares how they get run as long as they do.
2020-02-10 20:14:06 +01:00
BEGIN_TEST_CLASS(VtIoTests)
TEST_CLASS_PROPERTY(L"IsolationLevel", L"Class")
END_TEST_CLASS()
// General Tests:
TEST_METHOD(NoOpStartTest);
TEST_METHOD(ModeParsingTest);
TEST_METHOD(DtorTestJustEngine);
TEST_METHOD(DtorTestDeleteVtio);
TEST_METHOD(DtorTestStackAlloc);
TEST_METHOD(DtorTestStackAllocMany);
TEST_METHOD(RendererDtorAndThread);
#ifndef __INSIDE_WINDOWS
TEST_METHOD(RendererDtorAndThreadAndDx);
#endif
TEST_METHOD(BasicAnonymousPipeOpeningWithSignalChannelTest);
};
using namespace Microsoft::Console;
using namespace Microsoft::Console::VirtualTerminal;
using namespace Microsoft::Console::Render;
using namespace Microsoft::Console::Types;
void VtIoTests::NoOpStartTest()
{
VtIo vtio;
VERIFY_IS_FALSE(vtio.IsUsingVt());
Log::Comment(L"Verify we succeed at StartIfNeeded even if we weren't initialized");
VERIFY_SUCCEEDED(vtio.StartIfNeeded());
}
void VtIoTests::ModeParsingTest()
{
VtIoMode mode;
VERIFY_SUCCEEDED(VtIo::ParseIoMode(L"xterm", mode));
VERIFY_ARE_EQUAL(mode, VtIoMode::XTERM);
VERIFY_SUCCEEDED(VtIo::ParseIoMode(L"xterm-256color", mode));
VERIFY_ARE_EQUAL(mode, VtIoMode::XTERM_256);
VERIFY_SUCCEEDED(VtIo::ParseIoMode(L"xterm-ascii", mode));
VERIFY_ARE_EQUAL(mode, VtIoMode::XTERM_ASCII);
VERIFY_SUCCEEDED(VtIo::ParseIoMode(L"", mode));
VERIFY_ARE_EQUAL(mode, VtIoMode::XTERM_256);
VERIFY_FAILED(VtIo::ParseIoMode(L"garbage", mode));
VERIFY_ARE_EQUAL(mode, VtIoMode::INVALID);
}
Viewport SetUpViewport()
{
SMALL_RECT view = {};
view.Top = view.Left = 0;
view.Bottom = 31;
view.Right = 79;
return Viewport::FromInclusive(view);
}
void VtIoTests::DtorTestJustEngine()
{
Log::Comment(NoThrowString().Format(
L"This test is going to instantiate a bunch of VtIos in different \n"
L"scenarios to see if something causes a weird cleanup.\n"
L"It's here because of the strange nature of VtEngine having members\n"
L"that are only defined in UNIT_TESTING"));
Log::Comment(NoThrowString().Format(
L"New some engines and delete them"));
for (int i = 0; i < 25; ++i)
{
Log::Comment(NoThrowString().Format(
L"New/Delete loop #%d", i));
wil::unique_hfile hOutputFile;
hOutputFile.reset(INVALID_HANDLE_VALUE);
Improve the propagation of color attributes over ConPTY (#6506) This PR reimplements the VT rendering engines to do a better job of preserving the original color types when propagating attributes over ConPTY. For the 16-color renderers it provides better support for default colors and improves the efficiency of the color narrowing conversions. It also fixes problems with the ordering of character renditions that could result in attributes being dropped. Originally the base renderer would calculate the RGB color values and legacy/extended attributes up front, passing that data on to the active engine's `UpdateDrawingBrushes` method. With this new implementation, the renderer now just passes through the original `TextAttribute` along with an `IRenderData` interface, and leaves it to the engines to extract the information they need. The GDI and DirectX engines now have to lookup the RGB colors themselves (via simple `IRenderData` calls), but have no need for the other attributes. The VT engines extract the information that they need from the `TextAttribute`, instead of having to reverse engineer it from `COLORREF`s. The process for the 256-color Xterm engine starts with a check for default colors. If both foreground and background are default, it outputs a SGR 0 reset, and clears the `_lastTextAttribute` completely to make sure any reset state is reapplied. With that out the way, the foreground and background are updated (if changed) in one of 4 ways. They can either be a default value (SGR 39 and 49), a 16-color index (using ANSI or AIX sequences), a 256-color index, or a 24-bit RGB value (both using SGR 38 and 48 sequences). Then once the colors are accounted for, there is a separate step that handles the character rendition attributes (bold, italics, underline, etc.) This step must come _after_ the color sequences, in case a SGR reset is required, which would otherwise have cleared any character rendition attributes if it came last (which is what happened in the original implementation). The process for the 16-color engines is a little different. The target client in this case (Windows telnet) is incapable of setting default colors individually, so we need to output an SGR 0 reset if _either_ color has changed to default. With that out the way, we use the `TextColor::GetLegacyIndex` method to obtain an approximate 16-color index for each color, and apply the bold attribute by brightening the foreground index (setting bit 8) if the color type permits that. However, since Windows telnet only supports the 8 basic ANSI colors, the best we can do for bright colors is to output an SGR 1 attribute to get a bright foreground. There is nothing we can do about a bright background, so after that we just have to drop the high bit from the colors. If the resulting index values have changed from what they were before, we then output ANSI 8-color SGR sequences to update them. As with the 256-color engine, there is also a final step to handle the character rendition attributes. But in this case, the only supported attributes are underline and reversed video. Since the VT engines no longer depend on the active color table and default color values, there was quite a lot of code that could now be removed. This included the `IDefaultColorProvider` interface and implementations, the `Find(Nearest)TableIndex` functions, and also the associated HLS conversion and difference calculations. VALIDATION Other than simple API parameter changes, the majority of updates required in the unit tests were to correct assumptions about the way the colors should be rendered, which were the source of the narrowing bugs this PR was trying to fix. Like passing white on black to the `UpdateDrawingBrushes` API, and expecting it to output the default `SGR 0` sequence, or passing an RGB color and expecting an indexed SGR sequence. In addition to that, I've added some VT renderer tests to make sure the rendition attributes (bold, underline, etc) are correctly retained when a default color update causes an `SGR 0` sequence to be generated (the source of bug #3076). And I've extended the VT renderer color tests (both 256-color and 16-color) to make sure we're covering all of the different color types (default, RGB, and both forms of indexed colors). I've also tried to manually verify that all of the test cases in the linked bug reports (and their associated duplicates) are now fixed when this PR is applied. Closes #2661 Closes #3076 Closes #3717 Closes #5384 Closes #5864 This is only a partial fix for #293, but I suspect the remaining cases are unfixable.
2020-07-01 20:10:36 +02:00
auto pRenderer256 = new Xterm256Engine(std::move(hOutputFile), SetUpViewport());
Log::Comment(NoThrowString().Format(L"Made Xterm256Engine"));
delete pRenderer256;
Log::Comment(NoThrowString().Format(L"Deleted."));
hOutputFile.reset(INVALID_HANDLE_VALUE);
Improve the propagation of color attributes over ConPTY (#6506) This PR reimplements the VT rendering engines to do a better job of preserving the original color types when propagating attributes over ConPTY. For the 16-color renderers it provides better support for default colors and improves the efficiency of the color narrowing conversions. It also fixes problems with the ordering of character renditions that could result in attributes being dropped. Originally the base renderer would calculate the RGB color values and legacy/extended attributes up front, passing that data on to the active engine's `UpdateDrawingBrushes` method. With this new implementation, the renderer now just passes through the original `TextAttribute` along with an `IRenderData` interface, and leaves it to the engines to extract the information they need. The GDI and DirectX engines now have to lookup the RGB colors themselves (via simple `IRenderData` calls), but have no need for the other attributes. The VT engines extract the information that they need from the `TextAttribute`, instead of having to reverse engineer it from `COLORREF`s. The process for the 256-color Xterm engine starts with a check for default colors. If both foreground and background are default, it outputs a SGR 0 reset, and clears the `_lastTextAttribute` completely to make sure any reset state is reapplied. With that out the way, the foreground and background are updated (if changed) in one of 4 ways. They can either be a default value (SGR 39 and 49), a 16-color index (using ANSI or AIX sequences), a 256-color index, or a 24-bit RGB value (both using SGR 38 and 48 sequences). Then once the colors are accounted for, there is a separate step that handles the character rendition attributes (bold, italics, underline, etc.) This step must come _after_ the color sequences, in case a SGR reset is required, which would otherwise have cleared any character rendition attributes if it came last (which is what happened in the original implementation). The process for the 16-color engines is a little different. The target client in this case (Windows telnet) is incapable of setting default colors individually, so we need to output an SGR 0 reset if _either_ color has changed to default. With that out the way, we use the `TextColor::GetLegacyIndex` method to obtain an approximate 16-color index for each color, and apply the bold attribute by brightening the foreground index (setting bit 8) if the color type permits that. However, since Windows telnet only supports the 8 basic ANSI colors, the best we can do for bright colors is to output an SGR 1 attribute to get a bright foreground. There is nothing we can do about a bright background, so after that we just have to drop the high bit from the colors. If the resulting index values have changed from what they were before, we then output ANSI 8-color SGR sequences to update them. As with the 256-color engine, there is also a final step to handle the character rendition attributes. But in this case, the only supported attributes are underline and reversed video. Since the VT engines no longer depend on the active color table and default color values, there was quite a lot of code that could now be removed. This included the `IDefaultColorProvider` interface and implementations, the `Find(Nearest)TableIndex` functions, and also the associated HLS conversion and difference calculations. VALIDATION Other than simple API parameter changes, the majority of updates required in the unit tests were to correct assumptions about the way the colors should be rendered, which were the source of the narrowing bugs this PR was trying to fix. Like passing white on black to the `UpdateDrawingBrushes` API, and expecting it to output the default `SGR 0` sequence, or passing an RGB color and expecting an indexed SGR sequence. In addition to that, I've added some VT renderer tests to make sure the rendition attributes (bold, underline, etc) are correctly retained when a default color update causes an `SGR 0` sequence to be generated (the source of bug #3076). And I've extended the VT renderer color tests (both 256-color and 16-color) to make sure we're covering all of the different color types (default, RGB, and both forms of indexed colors). I've also tried to manually verify that all of the test cases in the linked bug reports (and their associated duplicates) are now fixed when this PR is applied. Closes #2661 Closes #3076 Closes #3717 Closes #5384 Closes #5864 This is only a partial fix for #293, but I suspect the remaining cases are unfixable.
2020-07-01 20:10:36 +02:00
auto pRenderEngineXterm = new XtermEngine(std::move(hOutputFile), SetUpViewport(), false);
Log::Comment(NoThrowString().Format(L"Made XtermEngine"));
delete pRenderEngineXterm;
Log::Comment(NoThrowString().Format(L"Deleted."));
hOutputFile.reset(INVALID_HANDLE_VALUE);
Improve the propagation of color attributes over ConPTY (#6506) This PR reimplements the VT rendering engines to do a better job of preserving the original color types when propagating attributes over ConPTY. For the 16-color renderers it provides better support for default colors and improves the efficiency of the color narrowing conversions. It also fixes problems with the ordering of character renditions that could result in attributes being dropped. Originally the base renderer would calculate the RGB color values and legacy/extended attributes up front, passing that data on to the active engine's `UpdateDrawingBrushes` method. With this new implementation, the renderer now just passes through the original `TextAttribute` along with an `IRenderData` interface, and leaves it to the engines to extract the information they need. The GDI and DirectX engines now have to lookup the RGB colors themselves (via simple `IRenderData` calls), but have no need for the other attributes. The VT engines extract the information that they need from the `TextAttribute`, instead of having to reverse engineer it from `COLORREF`s. The process for the 256-color Xterm engine starts with a check for default colors. If both foreground and background are default, it outputs a SGR 0 reset, and clears the `_lastTextAttribute` completely to make sure any reset state is reapplied. With that out the way, the foreground and background are updated (if changed) in one of 4 ways. They can either be a default value (SGR 39 and 49), a 16-color index (using ANSI or AIX sequences), a 256-color index, or a 24-bit RGB value (both using SGR 38 and 48 sequences). Then once the colors are accounted for, there is a separate step that handles the character rendition attributes (bold, italics, underline, etc.) This step must come _after_ the color sequences, in case a SGR reset is required, which would otherwise have cleared any character rendition attributes if it came last (which is what happened in the original implementation). The process for the 16-color engines is a little different. The target client in this case (Windows telnet) is incapable of setting default colors individually, so we need to output an SGR 0 reset if _either_ color has changed to default. With that out the way, we use the `TextColor::GetLegacyIndex` method to obtain an approximate 16-color index for each color, and apply the bold attribute by brightening the foreground index (setting bit 8) if the color type permits that. However, since Windows telnet only supports the 8 basic ANSI colors, the best we can do for bright colors is to output an SGR 1 attribute to get a bright foreground. There is nothing we can do about a bright background, so after that we just have to drop the high bit from the colors. If the resulting index values have changed from what they were before, we then output ANSI 8-color SGR sequences to update them. As with the 256-color engine, there is also a final step to handle the character rendition attributes. But in this case, the only supported attributes are underline and reversed video. Since the VT engines no longer depend on the active color table and default color values, there was quite a lot of code that could now be removed. This included the `IDefaultColorProvider` interface and implementations, the `Find(Nearest)TableIndex` functions, and also the associated HLS conversion and difference calculations. VALIDATION Other than simple API parameter changes, the majority of updates required in the unit tests were to correct assumptions about the way the colors should be rendered, which were the source of the narrowing bugs this PR was trying to fix. Like passing white on black to the `UpdateDrawingBrushes` API, and expecting it to output the default `SGR 0` sequence, or passing an RGB color and expecting an indexed SGR sequence. In addition to that, I've added some VT renderer tests to make sure the rendition attributes (bold, underline, etc) are correctly retained when a default color update causes an `SGR 0` sequence to be generated (the source of bug #3076). And I've extended the VT renderer color tests (both 256-color and 16-color) to make sure we're covering all of the different color types (default, RGB, and both forms of indexed colors). I've also tried to manually verify that all of the test cases in the linked bug reports (and their associated duplicates) are now fixed when this PR is applied. Closes #2661 Closes #3076 Closes #3717 Closes #5384 Closes #5864 This is only a partial fix for #293, but I suspect the remaining cases are unfixable.
2020-07-01 20:10:36 +02:00
auto pRenderEngineXtermAscii = new XtermEngine(std::move(hOutputFile), SetUpViewport(), true);
Log::Comment(NoThrowString().Format(L"Made XtermEngine"));
delete pRenderEngineXtermAscii;
Log::Comment(NoThrowString().Format(L"Deleted."));
}
}
void VtIoTests::DtorTestDeleteVtio()
{
Log::Comment(NoThrowString().Format(
L"This test is going to instantiate a bunch of VtIos in different \n"
L"scenarios to see if something causes a weird cleanup.\n"
L"It's here because of the strange nature of VtEngine having members\n"
L"that are only defined in UNIT_TESTING"));
Log::Comment(NoThrowString().Format(
L"New some engines and delete them"));
for (int i = 0; i < 25; ++i)
{
Log::Comment(NoThrowString().Format(
L"New/Delete loop #%d", i));
wil::unique_hfile hOutputFile = wil::unique_hfile(INVALID_HANDLE_VALUE);
hOutputFile.reset(INVALID_HANDLE_VALUE);
VtIo* vtio = new VtIo();
Log::Comment(NoThrowString().Format(L"Made VtIo"));
vtio->_pVtRenderEngine = std::make_unique<Xterm256Engine>(std::move(hOutputFile),
Improve the propagation of color attributes over ConPTY (#6506) This PR reimplements the VT rendering engines to do a better job of preserving the original color types when propagating attributes over ConPTY. For the 16-color renderers it provides better support for default colors and improves the efficiency of the color narrowing conversions. It also fixes problems with the ordering of character renditions that could result in attributes being dropped. Originally the base renderer would calculate the RGB color values and legacy/extended attributes up front, passing that data on to the active engine's `UpdateDrawingBrushes` method. With this new implementation, the renderer now just passes through the original `TextAttribute` along with an `IRenderData` interface, and leaves it to the engines to extract the information they need. The GDI and DirectX engines now have to lookup the RGB colors themselves (via simple `IRenderData` calls), but have no need for the other attributes. The VT engines extract the information that they need from the `TextAttribute`, instead of having to reverse engineer it from `COLORREF`s. The process for the 256-color Xterm engine starts with a check for default colors. If both foreground and background are default, it outputs a SGR 0 reset, and clears the `_lastTextAttribute` completely to make sure any reset state is reapplied. With that out the way, the foreground and background are updated (if changed) in one of 4 ways. They can either be a default value (SGR 39 and 49), a 16-color index (using ANSI or AIX sequences), a 256-color index, or a 24-bit RGB value (both using SGR 38 and 48 sequences). Then once the colors are accounted for, there is a separate step that handles the character rendition attributes (bold, italics, underline, etc.) This step must come _after_ the color sequences, in case a SGR reset is required, which would otherwise have cleared any character rendition attributes if it came last (which is what happened in the original implementation). The process for the 16-color engines is a little different. The target client in this case (Windows telnet) is incapable of setting default colors individually, so we need to output an SGR 0 reset if _either_ color has changed to default. With that out the way, we use the `TextColor::GetLegacyIndex` method to obtain an approximate 16-color index for each color, and apply the bold attribute by brightening the foreground index (setting bit 8) if the color type permits that. However, since Windows telnet only supports the 8 basic ANSI colors, the best we can do for bright colors is to output an SGR 1 attribute to get a bright foreground. There is nothing we can do about a bright background, so after that we just have to drop the high bit from the colors. If the resulting index values have changed from what they were before, we then output ANSI 8-color SGR sequences to update them. As with the 256-color engine, there is also a final step to handle the character rendition attributes. But in this case, the only supported attributes are underline and reversed video. Since the VT engines no longer depend on the active color table and default color values, there was quite a lot of code that could now be removed. This included the `IDefaultColorProvider` interface and implementations, the `Find(Nearest)TableIndex` functions, and also the associated HLS conversion and difference calculations. VALIDATION Other than simple API parameter changes, the majority of updates required in the unit tests were to correct assumptions about the way the colors should be rendered, which were the source of the narrowing bugs this PR was trying to fix. Like passing white on black to the `UpdateDrawingBrushes` API, and expecting it to output the default `SGR 0` sequence, or passing an RGB color and expecting an indexed SGR sequence. In addition to that, I've added some VT renderer tests to make sure the rendition attributes (bold, underline, etc) are correctly retained when a default color update causes an `SGR 0` sequence to be generated (the source of bug #3076). And I've extended the VT renderer color tests (both 256-color and 16-color) to make sure we're covering all of the different color types (default, RGB, and both forms of indexed colors). I've also tried to manually verify that all of the test cases in the linked bug reports (and their associated duplicates) are now fixed when this PR is applied. Closes #2661 Closes #3076 Closes #3717 Closes #5384 Closes #5864 This is only a partial fix for #293, but I suspect the remaining cases are unfixable.
2020-07-01 20:10:36 +02:00
SetUpViewport());
Log::Comment(NoThrowString().Format(L"Made Xterm256Engine"));
delete vtio;
Log::Comment(NoThrowString().Format(L"Deleted."));
hOutputFile = wil::unique_hfile(INVALID_HANDLE_VALUE);
vtio = new VtIo();
Log::Comment(NoThrowString().Format(L"Made VtIo"));
vtio->_pVtRenderEngine = std::make_unique<XtermEngine>(std::move(hOutputFile),
SetUpViewport(),
false);
Log::Comment(NoThrowString().Format(L"Made XtermEngine"));
delete vtio;
Log::Comment(NoThrowString().Format(L"Deleted."));
hOutputFile = wil::unique_hfile(INVALID_HANDLE_VALUE);
vtio = new VtIo();
Log::Comment(NoThrowString().Format(L"Made VtIo"));
vtio->_pVtRenderEngine = std::make_unique<XtermEngine>(std::move(hOutputFile),
SetUpViewport(),
true);
Log::Comment(NoThrowString().Format(L"Made XtermEngine"));
delete vtio;
Log::Comment(NoThrowString().Format(L"Deleted."));
}
}
void VtIoTests::DtorTestStackAlloc()
{
Log::Comment(NoThrowString().Format(
L"This test is going to instantiate a bunch of VtIos in different \n"
L"scenarios to see if something causes a weird cleanup.\n"
L"It's here because of the strange nature of VtEngine having members\n"
L"that are only defined in UNIT_TESTING"));
Log::Comment(NoThrowString().Format(
L"make some engines and let them fall out of scope"));
for (int i = 0; i < 25; ++i)
{
Log::Comment(NoThrowString().Format(
L"Scope Exit Auto cleanup #%d", i));
wil::unique_hfile hOutputFile;
hOutputFile.reset(INVALID_HANDLE_VALUE);
{
VtIo vtio;
vtio._pVtRenderEngine = std::make_unique<Xterm256Engine>(std::move(hOutputFile),
Improve the propagation of color attributes over ConPTY (#6506) This PR reimplements the VT rendering engines to do a better job of preserving the original color types when propagating attributes over ConPTY. For the 16-color renderers it provides better support for default colors and improves the efficiency of the color narrowing conversions. It also fixes problems with the ordering of character renditions that could result in attributes being dropped. Originally the base renderer would calculate the RGB color values and legacy/extended attributes up front, passing that data on to the active engine's `UpdateDrawingBrushes` method. With this new implementation, the renderer now just passes through the original `TextAttribute` along with an `IRenderData` interface, and leaves it to the engines to extract the information they need. The GDI and DirectX engines now have to lookup the RGB colors themselves (via simple `IRenderData` calls), but have no need for the other attributes. The VT engines extract the information that they need from the `TextAttribute`, instead of having to reverse engineer it from `COLORREF`s. The process for the 256-color Xterm engine starts with a check for default colors. If both foreground and background are default, it outputs a SGR 0 reset, and clears the `_lastTextAttribute` completely to make sure any reset state is reapplied. With that out the way, the foreground and background are updated (if changed) in one of 4 ways. They can either be a default value (SGR 39 and 49), a 16-color index (using ANSI or AIX sequences), a 256-color index, or a 24-bit RGB value (both using SGR 38 and 48 sequences). Then once the colors are accounted for, there is a separate step that handles the character rendition attributes (bold, italics, underline, etc.) This step must come _after_ the color sequences, in case a SGR reset is required, which would otherwise have cleared any character rendition attributes if it came last (which is what happened in the original implementation). The process for the 16-color engines is a little different. The target client in this case (Windows telnet) is incapable of setting default colors individually, so we need to output an SGR 0 reset if _either_ color has changed to default. With that out the way, we use the `TextColor::GetLegacyIndex` method to obtain an approximate 16-color index for each color, and apply the bold attribute by brightening the foreground index (setting bit 8) if the color type permits that. However, since Windows telnet only supports the 8 basic ANSI colors, the best we can do for bright colors is to output an SGR 1 attribute to get a bright foreground. There is nothing we can do about a bright background, so after that we just have to drop the high bit from the colors. If the resulting index values have changed from what they were before, we then output ANSI 8-color SGR sequences to update them. As with the 256-color engine, there is also a final step to handle the character rendition attributes. But in this case, the only supported attributes are underline and reversed video. Since the VT engines no longer depend on the active color table and default color values, there was quite a lot of code that could now be removed. This included the `IDefaultColorProvider` interface and implementations, the `Find(Nearest)TableIndex` functions, and also the associated HLS conversion and difference calculations. VALIDATION Other than simple API parameter changes, the majority of updates required in the unit tests were to correct assumptions about the way the colors should be rendered, which were the source of the narrowing bugs this PR was trying to fix. Like passing white on black to the `UpdateDrawingBrushes` API, and expecting it to output the default `SGR 0` sequence, or passing an RGB color and expecting an indexed SGR sequence. In addition to that, I've added some VT renderer tests to make sure the rendition attributes (bold, underline, etc) are correctly retained when a default color update causes an `SGR 0` sequence to be generated (the source of bug #3076). And I've extended the VT renderer color tests (both 256-color and 16-color) to make sure we're covering all of the different color types (default, RGB, and both forms of indexed colors). I've also tried to manually verify that all of the test cases in the linked bug reports (and their associated duplicates) are now fixed when this PR is applied. Closes #2661 Closes #3076 Closes #3717 Closes #5384 Closes #5864 This is only a partial fix for #293, but I suspect the remaining cases are unfixable.
2020-07-01 20:10:36 +02:00
SetUpViewport());
}
hOutputFile.reset(INVALID_HANDLE_VALUE);
{
VtIo vtio;
vtio._pVtRenderEngine = std::make_unique<XtermEngine>(std::move(hOutputFile),
SetUpViewport(),
false);
}
hOutputFile.reset(INVALID_HANDLE_VALUE);
{
VtIo vtio;
vtio._pVtRenderEngine = std::make_unique<XtermEngine>(std::move(hOutputFile),
SetUpViewport(),
true);
}
}
}
void VtIoTests::DtorTestStackAllocMany()
{
Log::Comment(NoThrowString().Format(
L"This test is going to instantiate a bunch of VtIos in different \n"
L"scenarios to see if something causes a weird cleanup.\n"
L"It's here because of the strange nature of VtEngine having members\n"
L"that are only defined in UNIT_TESTING"));
Log::Comment(NoThrowString().Format(
L"Try an make a whole bunch all at once, and have them all fall out of scope at once."));
for (int i = 0; i < 25; ++i)
{
Log::Comment(NoThrowString().Format(
L"Multiple engines, one scope loop #%d", i));
wil::unique_hfile hOutputFile;
{
hOutputFile.reset(INVALID_HANDLE_VALUE);
VtIo vtio1;
vtio1._pVtRenderEngine = std::make_unique<Xterm256Engine>(std::move(hOutputFile),
Improve the propagation of color attributes over ConPTY (#6506) This PR reimplements the VT rendering engines to do a better job of preserving the original color types when propagating attributes over ConPTY. For the 16-color renderers it provides better support for default colors and improves the efficiency of the color narrowing conversions. It also fixes problems with the ordering of character renditions that could result in attributes being dropped. Originally the base renderer would calculate the RGB color values and legacy/extended attributes up front, passing that data on to the active engine's `UpdateDrawingBrushes` method. With this new implementation, the renderer now just passes through the original `TextAttribute` along with an `IRenderData` interface, and leaves it to the engines to extract the information they need. The GDI and DirectX engines now have to lookup the RGB colors themselves (via simple `IRenderData` calls), but have no need for the other attributes. The VT engines extract the information that they need from the `TextAttribute`, instead of having to reverse engineer it from `COLORREF`s. The process for the 256-color Xterm engine starts with a check for default colors. If both foreground and background are default, it outputs a SGR 0 reset, and clears the `_lastTextAttribute` completely to make sure any reset state is reapplied. With that out the way, the foreground and background are updated (if changed) in one of 4 ways. They can either be a default value (SGR 39 and 49), a 16-color index (using ANSI or AIX sequences), a 256-color index, or a 24-bit RGB value (both using SGR 38 and 48 sequences). Then once the colors are accounted for, there is a separate step that handles the character rendition attributes (bold, italics, underline, etc.) This step must come _after_ the color sequences, in case a SGR reset is required, which would otherwise have cleared any character rendition attributes if it came last (which is what happened in the original implementation). The process for the 16-color engines is a little different. The target client in this case (Windows telnet) is incapable of setting default colors individually, so we need to output an SGR 0 reset if _either_ color has changed to default. With that out the way, we use the `TextColor::GetLegacyIndex` method to obtain an approximate 16-color index for each color, and apply the bold attribute by brightening the foreground index (setting bit 8) if the color type permits that. However, since Windows telnet only supports the 8 basic ANSI colors, the best we can do for bright colors is to output an SGR 1 attribute to get a bright foreground. There is nothing we can do about a bright background, so after that we just have to drop the high bit from the colors. If the resulting index values have changed from what they were before, we then output ANSI 8-color SGR sequences to update them. As with the 256-color engine, there is also a final step to handle the character rendition attributes. But in this case, the only supported attributes are underline and reversed video. Since the VT engines no longer depend on the active color table and default color values, there was quite a lot of code that could now be removed. This included the `IDefaultColorProvider` interface and implementations, the `Find(Nearest)TableIndex` functions, and also the associated HLS conversion and difference calculations. VALIDATION Other than simple API parameter changes, the majority of updates required in the unit tests were to correct assumptions about the way the colors should be rendered, which were the source of the narrowing bugs this PR was trying to fix. Like passing white on black to the `UpdateDrawingBrushes` API, and expecting it to output the default `SGR 0` sequence, or passing an RGB color and expecting an indexed SGR sequence. In addition to that, I've added some VT renderer tests to make sure the rendition attributes (bold, underline, etc) are correctly retained when a default color update causes an `SGR 0` sequence to be generated (the source of bug #3076). And I've extended the VT renderer color tests (both 256-color and 16-color) to make sure we're covering all of the different color types (default, RGB, and both forms of indexed colors). I've also tried to manually verify that all of the test cases in the linked bug reports (and their associated duplicates) are now fixed when this PR is applied. Closes #2661 Closes #3076 Closes #3717 Closes #5384 Closes #5864 This is only a partial fix for #293, but I suspect the remaining cases are unfixable.
2020-07-01 20:10:36 +02:00
SetUpViewport());
hOutputFile.reset(INVALID_HANDLE_VALUE);
VtIo vtio2;
vtio2._pVtRenderEngine = std::make_unique<XtermEngine>(std::move(hOutputFile),
SetUpViewport(),
false);
hOutputFile.reset(INVALID_HANDLE_VALUE);
VtIo vtio3;
vtio3._pVtRenderEngine = std::make_unique<XtermEngine>(std::move(hOutputFile),
SetUpViewport(),
true);
}
}
}
class MockRenderData : public IRenderData, IUiaData
{
public:
Microsoft::Console::Types::Viewport GetViewport() noexcept override
{
return Microsoft::Console::Types::Viewport{};
}
COORD GetTextBufferEndPosition() const noexcept override
{
return COORD{};
}
const TextBuffer& GetTextBuffer() noexcept override
{
FAIL_FAST_HR(E_NOTIMPL);
}
const FontInfo& GetFontInfo() noexcept override
{
FAIL_FAST_HR(E_NOTIMPL);
}
std::vector<Microsoft::Console::Types::Viewport> GetSelectionRects() noexcept override
{
return std::vector<Microsoft::Console::Types::Viewport>{};
}
void LockConsole() noexcept override
{
}
void UnlockConsole() noexcept override
{
}
const TextAttribute GetDefaultBrushColors() noexcept override
{
return TextAttribute{};
}
std::pair<COLORREF, COLORREF> GetAttributeColors(const TextAttribute& /*attr*/) const noexcept override
{
return std::make_pair(COLORREF{}, COLORREF{});
}
COORD GetCursorPosition() const noexcept override
{
return COORD{};
}
bool IsCursorVisible() const noexcept override
{
return false;
}
bool IsCursorOn() const noexcept override
{
return false;
}
ULONG GetCursorHeight() const noexcept override
{
return 42ul;
}
CursorType GetCursorStyle() const noexcept override
{
return CursorType::FullBox;
}
ULONG GetCursorPixelWidth() const noexcept override
{
return 12ul;
}
COLORREF GetCursorColor() const noexcept override
{
return COLORREF{};
}
bool IsCursorDoubleWidth() const override
{
return false;
}
bool IsScreenReversed() const noexcept override
{
return false;
}
const std::vector<RenderOverlay> GetOverlays() const noexcept override
{
return std::vector<RenderOverlay>{};
}
const bool IsGridLineDrawingAllowed() noexcept override
{
return false;
}
const std::wstring GetConsoleTitle() const noexcept override
{
return std::wstring{};
}
const bool IsSelectionActive() const override
{
return false;
}
const bool IsBlockSelection() const noexcept override
{
return false;
}
void ClearSelection() override
{
}
void SelectNewRegion(const COORD /*coordStart*/, const COORD /*coordEnd*/) override
{
}
const COORD GetSelectionAnchor() const noexcept
{
return COORD{};
}
const COORD GetSelectionEnd() const noexcept
{
return COORD{};
}
void ColorSelection(const COORD /*coordSelectionStart*/, const COORD /*coordSelectionEnd*/, const TextAttribute /*attr*/)
{
}
OSC 8 support for conhost and terminal (#7251) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Conhost can now support OSC8 sequences (as specified [here](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda)). Terminal also supports those sequences and additionally hyperlinks can be opened by Ctrl+LeftClicking on them. <!-- Other than the issue solved, is this relevant to any other issues/existing PRs? --> ## References #204 <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist * [X] Closes #204 * [ ] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [ ] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx <!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments Added support to: - parse OSC8 sequences and extract URIs from them (conhost and terminal) - add hyperlink uri data to textbuffer/screeninformation, associated with a hyperlink id (conhost and terminal) - attach hyperlink ids to text to allow for uri extraction from the textbuffer/screeninformation (conhost and terminal) - process ctrl+leftclick to open a hyperlink in the clicked region if present <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed Open up a PowerShell tab and type ```PowerShell ${ESC}=[char]27 Write-Host "${ESC}]8;;https://github.com/microsoft/terminal${ESC}\This is a link!${ESC}]8;;${ESC}\" ``` Ctrl+LeftClick on the link correctly brings you to the terminal page on github ![hyperlink](https://user-images.githubusercontent.com/26824113/89953536-45a6f580-dbfd-11ea-8e0d-8a3cd25c634a.gif)
2020-09-03 19:52:39 +02:00
const std::wstring GetHyperlinkUri(uint16_t /*id*/) const noexcept
{
return {};
}
const std::wstring GetHyperlinkCustomId(uint16_t /*id*/) const noexcept
{
return {};
}
const std::vector<size_t> GetPatternId(const COORD /*location*/) const noexcept
{
return {};
}
};
void VtIoTests::RendererDtorAndThread()
{
Log::Comment(NoThrowString().Format(
L"Test deleting a Renderer a bunch of times"));
for (int i = 0; i < 16; ++i)
{
auto data = std::make_unique<MockRenderData>();
auto thread = std::make_unique<Microsoft::Console::Render::RenderThread>();
auto* pThread = thread.get();
auto pRenderer = std::make_unique<Microsoft::Console::Render::Renderer>(data.get(), nullptr, 0, std::move(thread));
VERIFY_SUCCEEDED(pThread->Initialize(pRenderer.get()));
// Sleep for a hot sec to make sure the thread starts before we enable painting
// If you don't, the thread might wait on the paint enabled event AFTER
// EnablePainting gets called, and if that happens, then the thread will
// never get destructed. This will only ever happen in the vstest test runner,
// which is what CI uses.
Move tests to invoke `te.exe` directly instead of using VSTest runner (#4490) Moves the tests from using the `vstest.console.exe` route to just using `te.exe`. PROs: - `te.exe` is significantly faster for running tests because the TAEF/VSTest adapter isn't great. - Running through `te.exe` is closer to what our developers are doing on their dev boxes - `te.exe` is how they run in the Windows gates. - `te.exe` doesn't seem to have the sporadic `0x6` error code thrown during the tests where somehow the console handles get lost - `te.exe` doesn't seem to repro the other intermittent issues that we have been having that are inscrutable. - Fewer processes in the tree (te is running anyway under `vstest.console.exe`, just indirected a lot - The log outputs scroll live with all our logging messages instead of suppressing everything until there's a failure - The log output is actually in the order things are happening versus vstest. CONs: - No more code coverage. - No more test records in the ADO build/test panel. - Tests really won't work inside Visual Studio at all. - The log files are really big now - Testing is not a test task anymore, just another script. Refuting each CON: - We didn't read the code coverage numbers - We didn't look at the ADO test panel results or build-over-build velocities - Tests didn't really work inside Visual Studio anyway unless you did the right incantations under the full moon. - We could tone down the logging if we wanted at either the te.exe execution time (with a switch) or by declaring properties in the tests/classes/modules that are very verbose to not log unless it fails. - I don't think anyone cares how they get run as long as they do.
2020-02-10 20:14:06 +01:00
/*Sleep(500);*/
pThread->EnablePainting();
pRenderer->TriggerTeardown();
pRenderer.reset();
}
}
#ifndef __INSIDE_WINDOWS
void VtIoTests::RendererDtorAndThreadAndDx()
{
Log::Comment(NoThrowString().Format(
L"Test deleting a Renderer a bunch of times"));
for (int i = 0; i < 16; ++i)
{
auto data = std::make_unique<MockRenderData>();
auto thread = std::make_unique<Microsoft::Console::Render::RenderThread>();
auto* pThread = thread.get();
auto pRenderer = std::make_unique<Microsoft::Console::Render::Renderer>(data.get(), nullptr, 0, std::move(thread));
VERIFY_SUCCEEDED(pThread->Initialize(pRenderer.get()));
auto dxEngine = std::make_unique<::Microsoft::Console::Render::DxEngine>();
pRenderer->AddRenderEngine(dxEngine.get());
// Sleep for a hot sec to make sure the thread starts before we enable painting
// If you don't, the thread might wait on the paint enabled event AFTER
// EnablePainting gets called, and if that happens, then the thread will
// never get destructed. This will only ever happen in the vstest test runner,
// which is what CI uses.
Move tests to invoke `te.exe` directly instead of using VSTest runner (#4490) Moves the tests from using the `vstest.console.exe` route to just using `te.exe`. PROs: - `te.exe` is significantly faster for running tests because the TAEF/VSTest adapter isn't great. - Running through `te.exe` is closer to what our developers are doing on their dev boxes - `te.exe` is how they run in the Windows gates. - `te.exe` doesn't seem to have the sporadic `0x6` error code thrown during the tests where somehow the console handles get lost - `te.exe` doesn't seem to repro the other intermittent issues that we have been having that are inscrutable. - Fewer processes in the tree (te is running anyway under `vstest.console.exe`, just indirected a lot - The log outputs scroll live with all our logging messages instead of suppressing everything until there's a failure - The log output is actually in the order things are happening versus vstest. CONs: - No more code coverage. - No more test records in the ADO build/test panel. - Tests really won't work inside Visual Studio at all. - The log files are really big now - Testing is not a test task anymore, just another script. Refuting each CON: - We didn't read the code coverage numbers - We didn't look at the ADO test panel results or build-over-build velocities - Tests didn't really work inside Visual Studio anyway unless you did the right incantations under the full moon. - We could tone down the logging if we wanted at either the te.exe execution time (with a switch) or by declaring properties in the tests/classes/modules that are very verbose to not log unless it fails. - I don't think anyone cares how they get run as long as they do.
2020-02-10 20:14:06 +01:00
/*Sleep(500);*/
pThread->EnablePainting();
pRenderer->TriggerTeardown();
pRenderer.reset();
}
}
#endif
void VtIoTests::BasicAnonymousPipeOpeningWithSignalChannelTest()
{
Log::Comment(L"Test using anonymous pipes for the input and adding a signal channel.");
Log::Comment(L"\tcreating pipes");
wil::unique_handle inPipeReadSide;
wil::unique_handle inPipeWriteSide;
wil::unique_handle outPipeReadSide;
wil::unique_handle outPipeWriteSide;
wil::unique_handle signalPipeReadSide;
wil::unique_handle signalPipeWriteSide;
VERIFY_WIN32_BOOL_SUCCEEDED(CreatePipe(&inPipeReadSide, &inPipeWriteSide, nullptr, 0), L"Create anonymous in pipe.");
VERIFY_WIN32_BOOL_SUCCEEDED(CreatePipe(&outPipeReadSide, &outPipeWriteSide, nullptr, 0), L"Create anonymous out pipe.");
VERIFY_WIN32_BOOL_SUCCEEDED(CreatePipe(&signalPipeReadSide, &signalPipeWriteSide, nullptr, 0), L"Create anonymous signal pipe.");
Log::Comment(L"\tinitializing vtio");
VtIo vtio;
VERIFY_IS_FALSE(vtio.IsUsingVt());
VERIFY_ARE_EQUAL(nullptr, vtio._pPtySignalInputThread);
VERIFY_SUCCEEDED(vtio._Initialize(inPipeReadSide.release(), outPipeWriteSide.release(), L"", signalPipeReadSide.release()));
VERIFY_SUCCEEDED(vtio.CreateAndStartSignalThread());
VERIFY_SUCCEEDED(vtio.CreateIoHandlers());
VERIFY_IS_TRUE(vtio.IsUsingVt());
VERIFY_ARE_NOT_EQUAL(nullptr, vtio._pPtySignalInputThread);
}