Files
Terminal.Gui/Tests/UnitTestsParallelizable/Drivers/OutputBaseTests.cs

274 lines
9.5 KiB
C#
Raw Blame History

#nullable enable
namespace DriverTests;
public class OutputBaseTests
{
[Fact]
public void ToAnsi_SingleCell_NoAttribute_ReturnsGraphemeAndNewline ()
{
// Arrange
var output = new FakeOutput ();
IOutputBuffer buffer = output.LastBuffer!;
buffer.SetSize (1, 1);
// Act
buffer.AddStr ("A");
string ansi = output.ToAnsi (buffer);
// Assert: single grapheme plus newline (BuildAnsiForRegion appends a newline per row)
Assert.Contains ("A" + Environment.NewLine, ansi);
}
[Theory]
[InlineData (true, false)]
[InlineData (true, true)]
[InlineData (false, false)]
[InlineData (false, true)]
public void ToAnsi_WithAttribute_AppendsCorrectColorSequence_BasedOnIsLegacyConsole_And_Force16Colors (bool isLegacyConsole, bool force16Colors)
{
// Arrange
var output = new FakeOutput { IsLegacyConsole = isLegacyConsole };
// Create DriverImpl and associate it with the FakeOutput to test Sixel output
IDriver driver = new DriverImpl (
new FakeInputProcessor (null!),
new OutputBufferImpl (),
output,
new (new AnsiResponseParser ()),
new SizeMonitorImpl (output));
driver.Force16Colors = force16Colors;
IOutputBuffer buffer = output.LastBuffer!;
buffer.SetSize (1, 1);
// Use a known RGB color and attribute
var fg = new Color (1, 2, 3);
var bg = new Color (4, 5, 6);
buffer.CurrentAttribute = new Attribute (fg, bg);
buffer.AddStr ("X");
// Act
string ansi = output.ToAnsi (buffer);
// Assert: when true color expected, we should see the RGB CSI; otherwise we should see the 16-color CSI
if (!isLegacyConsole && !force16Colors)
{
Assert.Contains ("\u001b[38;2;1;2;3m", ansi);
}
else if (!isLegacyConsole && force16Colors)
{
var expected16 = EscSeqUtils.CSI_SetForegroundColor (fg.GetAnsiColorCode ());
Assert.Contains (expected16, ansi);
}
else
{
var expected16 = (ConsoleColor)fg.GetClosestNamedColor16 ();
Assert.Equal (ConsoleColor.Black, expected16);
Assert.DoesNotContain ('\u001b', ansi);
}
// Grapheme and newline should always be present
Assert.Contains ("X" + Environment.NewLine, ansi);
}
[Fact]
public void Write_WritesDirtyCellsAndClearsDirtyFlags ()
{
// Arrange
var output = new FakeOutput ();
IOutputBuffer buffer = output.LastBuffer!;
buffer.SetSize (2, 1);
// Mark two characters as dirty by writing them into the buffer
buffer.AddStr ("AB");
// Sanity: ensure cells are dirty before calling Write
Assert.True (buffer.Contents! [0, 0].IsDirty);
Assert.True (buffer.Contents! [0, 1].IsDirty);
// Act
output.Write (buffer); // calls OutputBase.Write via FakeOutput
// Assert: content was written to the fake output and dirty flags cleared
Assert.Contains ("AB", output.Output);
Assert.False (buffer.Contents! [0, 0].IsDirty);
Assert.False (buffer.Contents! [0, 1].IsDirty);
}
[Theory]
[InlineData (true)]
[InlineData (false)]
public void Write_Virtual_Or_NonVirtual_Uses_WriteToConsole_And_Clears_Dirty_Flags (bool isLegacyConsole)
{
// Arrange
// FakeOutput exposes this because it's in test scope
var output = new FakeOutput { IsLegacyConsole = isLegacyConsole };
IOutputBuffer buffer = output.LastBuffer!;
buffer.SetSize (3, 1);
// Write 'A' at col 0 and 'C' at col 2; leave col 1 untouched (not dirty)
buffer.Move (0, 0);
buffer.AddStr ("A");
buffer.Move (2, 0);
buffer.AddStr ("C");
// Confirm some dirtiness before to write
Assert.True (buffer.Contents! [0, 0].IsDirty);
Assert.True (buffer.Contents! [0, 2].IsDirty);
// Act
output.Write (buffer);
// Assert: both characters were written (use Contains to avoid CI side effects)
Assert.Contains ("A", output.Output);
Assert.Contains ("C", output.Output);
// Dirty flags cleared for the written cells
Assert.False (buffer.Contents! [0, 0].IsDirty);
Assert.False (buffer.Contents! [0, 2].IsDirty);
// Verify SetCursorPositionImpl was invoked by WriteToConsole (cursor set to a written column)
Assert.Equal (new Point (0, 0), output.GetCursorPosition ());
// Now write 'X' at col 0 to verify subsequent writes also work
buffer.Move (0, 0);
buffer.AddStr ("X");
// Confirm dirtiness state before to write
Assert.True (buffer.Contents! [0, 0].IsDirty);
Assert.False (buffer.Contents! [0, 2].IsDirty);
output.Write (buffer);
// Assert: both characters were written (use Contains to avoid CI side effects)
Assert.Contains ("A", output.Output);
Assert.Contains ("C", output.Output);
// Dirty flags cleared for the written cells
Assert.False (buffer.Contents! [0, 0].IsDirty);
Assert.False (buffer.Contents! [0, 2].IsDirty);
// Verify SetCursorPositionImpl was invoked by WriteToConsole (cursor set to a written column)
Assert.Equal (new Point (2, 0), output.GetCursorPosition ());
}
[Theory]
[InlineData (true)]
[InlineData (false)]
public void Write_Virtual_Or_NonVirtual_Uses_WriteToConsole_And_Clears_Dirty_Flags_Mixed_Graphemes (bool isLegacyConsole)
{
// Arrange
// FakeOutput exposes this because it's in test scope
var output = new FakeOutput { IsLegacyConsole = isLegacyConsole };
IOutputBuffer buffer = output.LastBuffer!;
buffer.SetSize (3, 1);
// Write '🦮' at col 0 and 'A' at col 3; leave col 1 untouched (not dirty)
buffer.Move (0, 0);
buffer.AddStr ("🦮A");
// Confirm some dirtiness before to write
Assert.True (buffer.Contents! [0, 0].IsDirty);
Assert.False (buffer.Contents! [0, 1].IsDirty);
Assert.True (buffer.Contents! [0, 2].IsDirty);
// Act
output.Write (buffer);
Assert.Contains ("🦮", output.Output);
Assert.Contains ("A", output.Output);
// Dirty flags cleared for the written cells
Assert.False (buffer.Contents! [0, 0].IsDirty);
Assert.False (buffer.Contents! [0, 1].IsDirty);
Assert.False (buffer.Contents! [0, 2].IsDirty);
Assert.Equal (new (0, 0), output.GetCursorPosition ());
// Now write 'X' at col 1 which replaces with the replacement character the col 0
buffer.Move (1, 0);
buffer.AddStr ("X");
// Confirm dirtiness state before to write
Assert.True (buffer.Contents! [0, 0].IsDirty);
Assert.True (buffer.Contents! [0, 1].IsDirty);
Assert.True (buffer.Contents! [0, 2].IsDirty);
output.Write (buffer);
Assert.Contains ("<22>", output.Output);
Assert.Contains ("X", output.Output);
// Dirty flags cleared for the written cells
Assert.False (buffer.Contents! [0, 0].IsDirty);
Assert.False (buffer.Contents! [0, 1].IsDirty);
Assert.False (buffer.Contents! [0, 2].IsDirty);
// Verify SetCursorPositionImpl was invoked by WriteToConsole (cursor set to a written column)
Assert.Equal (new (0, 0), output.GetCursorPosition ());
}
[Theory]
[InlineData (true)]
[InlineData (false)]
public void Write_EmitsSixelDataAndPositionsCursor (bool isLegacyConsole)
{
// Arrange
var output = new FakeOutput ();
IOutputBuffer buffer = output.LastBuffer!;
buffer.SetSize (1, 1);
// Ensure the buffer has some content so Write traverses rows
buffer.AddStr (".");
// Create a Sixel to render
var s = new SixelToRender
{
SixelData = "SIXEL-DATA",
ScreenPosition = new Point (4, 2)
};
// Create DriverImpl and associate it with the FakeOutput to test Sixel output
IDriver driver = new DriverImpl (
new FakeInputProcessor (null!),
new OutputBufferImpl (),
output,
new (new AnsiResponseParser ()),
new SizeMonitorImpl (output));
// Add the Sixel to the driver
driver.GetSixels ().Enqueue (s);
// FakeOutput exposes this because it's in test scope
output.IsLegacyConsole = isLegacyConsole;
// Act
output.Write (buffer);
if (!isLegacyConsole)
{
// Assert: Sixel data was emitted (use Contains to avoid equality/side-effects)
Assert.Contains ("SIXEL-DATA", output.Output);
// Cursor was moved to Sixel position
Assert.Equal (s.ScreenPosition, output.GetCursorPosition ());
}
else
{
// Assert: Sixel data was NOT emitted
Assert.DoesNotContain ("SIXEL-DATA", output.Output);
// Cursor was NOT moved to Sixel position
Assert.NotEqual (s.ScreenPosition, output.GetCursorPosition ());
}
IApplication app = Application.Create ();
app.Driver = driver;
Assert.Equal (driver.GetSixels (), app.Driver.GetSixels ());
app.Dispose ();
}
}