OutputBaseTests.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #nullable enable
  2. namespace DriverTests;
  3. public class OutputBaseTests
  4. {
  5. [Fact]
  6. public void ToAnsi_SingleCell_NoAttribute_ReturnsGraphemeAndNewline ()
  7. {
  8. // Arrange
  9. var output = new FakeOutput ();
  10. IOutputBuffer buffer = output.LastBuffer!;
  11. buffer.SetSize (1, 1);
  12. // Act
  13. buffer.AddStr ("A");
  14. string ansi = output.ToAnsi (buffer);
  15. // Assert: single grapheme plus newline (BuildAnsiForRegion appends a newline per row)
  16. Assert.Contains ("A" + Environment.NewLine, ansi);
  17. }
  18. [Theory]
  19. [InlineData (true, false)]
  20. [InlineData (true, true)]
  21. [InlineData (false, false)]
  22. [InlineData (false, true)]
  23. public void ToAnsi_WithAttribute_AppendsCorrectColorSequence_BasedOnIsLegacyConsole_And_Force16Colors (bool isLegacyConsole, bool force16Colors)
  24. {
  25. // Arrange
  26. var output = new FakeOutput { IsLegacyConsole = isLegacyConsole };
  27. // Create DriverImpl and associate it with the FakeOutput to test Sixel output
  28. IDriver driver = new DriverImpl (
  29. new FakeInputProcessor (null!),
  30. new OutputBufferImpl (),
  31. output,
  32. new (new AnsiResponseParser ()),
  33. new SizeMonitorImpl (output));
  34. driver.Force16Colors = force16Colors;
  35. IOutputBuffer buffer = output.LastBuffer!;
  36. buffer.SetSize (1, 1);
  37. // Use a known RGB color and attribute
  38. var fg = new Color (1, 2, 3);
  39. var bg = new Color (4, 5, 6);
  40. buffer.CurrentAttribute = new Attribute (fg, bg);
  41. buffer.AddStr ("X");
  42. // Act
  43. string ansi = output.ToAnsi (buffer);
  44. // Assert: when true color expected, we should see the RGB CSI; otherwise we should see the 16-color CSI
  45. if (!isLegacyConsole && !force16Colors)
  46. {
  47. Assert.Contains ("\u001b[38;2;1;2;3m", ansi);
  48. }
  49. else if (!isLegacyConsole && force16Colors)
  50. {
  51. var expected16 = EscSeqUtils.CSI_SetForegroundColor (fg.GetAnsiColorCode ());
  52. Assert.Contains (expected16, ansi);
  53. }
  54. else
  55. {
  56. var expected16 = (ConsoleColor)fg.GetClosestNamedColor16 ();
  57. Assert.Equal (ConsoleColor.Black, expected16);
  58. Assert.DoesNotContain ('\u001b', ansi);
  59. }
  60. // Grapheme and newline should always be present
  61. Assert.Contains ("X" + Environment.NewLine, ansi);
  62. }
  63. [Fact]
  64. public void Write_WritesDirtyCellsAndClearsDirtyFlags ()
  65. {
  66. // Arrange
  67. var output = new FakeOutput ();
  68. IOutputBuffer buffer = output.LastBuffer!;
  69. buffer.SetSize (2, 1);
  70. // Mark two characters as dirty by writing them into the buffer
  71. buffer.AddStr ("AB");
  72. // Sanity: ensure cells are dirty before calling Write
  73. Assert.True (buffer.Contents! [0, 0].IsDirty);
  74. Assert.True (buffer.Contents! [0, 1].IsDirty);
  75. // Act
  76. output.Write (buffer); // calls OutputBase.Write via FakeOutput
  77. // Assert: content was written to the fake output and dirty flags cleared
  78. Assert.Contains ("AB", output.Output);
  79. Assert.False (buffer.Contents! [0, 0].IsDirty);
  80. Assert.False (buffer.Contents! [0, 1].IsDirty);
  81. }
  82. [Theory]
  83. [InlineData (true)]
  84. [InlineData (false)]
  85. public void Write_Virtual_Or_NonVirtual_Uses_WriteToConsole_And_Clears_Dirty_Flags (bool isLegacyConsole)
  86. {
  87. // Arrange
  88. // FakeOutput exposes this because it's in test scope
  89. var output = new FakeOutput { IsLegacyConsole = isLegacyConsole };
  90. IOutputBuffer buffer = output.LastBuffer!;
  91. buffer.SetSize (3, 1);
  92. // Write 'A' at col 0 and 'C' at col 2; leave col 1 untouched (not dirty)
  93. buffer.Move (0, 0);
  94. buffer.AddStr ("A");
  95. buffer.Move (2, 0);
  96. buffer.AddStr ("C");
  97. // Confirm some dirtiness before to write
  98. Assert.True (buffer.Contents! [0, 0].IsDirty);
  99. Assert.True (buffer.Contents! [0, 2].IsDirty);
  100. // Act
  101. output.Write (buffer);
  102. // Assert: both characters were written (use Contains to avoid CI side effects)
  103. Assert.Contains ("A", output.Output);
  104. Assert.Contains ("C", output.Output);
  105. // Dirty flags cleared for the written cells
  106. Assert.False (buffer.Contents! [0, 0].IsDirty);
  107. Assert.False (buffer.Contents! [0, 2].IsDirty);
  108. // Verify SetCursorPositionImpl was invoked by WriteToConsole (cursor set to a written column)
  109. Assert.Equal (new Point (0, 0), output.GetCursorPosition ());
  110. // Now write 'X' at col 0 to verify subsequent writes also work
  111. buffer.Move (0, 0);
  112. buffer.AddStr ("X");
  113. // Confirm dirtiness state before to write
  114. Assert.True (buffer.Contents! [0, 0].IsDirty);
  115. Assert.False (buffer.Contents! [0, 2].IsDirty);
  116. output.Write (buffer);
  117. // Assert: both characters were written (use Contains to avoid CI side effects)
  118. Assert.Contains ("A", output.Output);
  119. Assert.Contains ("C", output.Output);
  120. // Dirty flags cleared for the written cells
  121. Assert.False (buffer.Contents! [0, 0].IsDirty);
  122. Assert.False (buffer.Contents! [0, 2].IsDirty);
  123. // Verify SetCursorPositionImpl was invoked by WriteToConsole (cursor set to a written column)
  124. Assert.Equal (new Point (2, 0), output.GetCursorPosition ());
  125. }
  126. [Theory]
  127. [InlineData (true)]
  128. [InlineData (false)]
  129. public void Write_Virtual_Or_NonVirtual_Uses_WriteToConsole_And_Clears_Dirty_Flags_Mixed_Graphemes (bool isLegacyConsole)
  130. {
  131. // Arrange
  132. // FakeOutput exposes this because it's in test scope
  133. var output = new FakeOutput { IsLegacyConsole = isLegacyConsole };
  134. IOutputBuffer buffer = output.LastBuffer!;
  135. buffer.SetSize (3, 1);
  136. // Write '🦮' at col 0 and 'A' at col 3; leave col 1 untouched (not dirty)
  137. buffer.Move (0, 0);
  138. buffer.AddStr ("🦮A");
  139. // Confirm some dirtiness before to write
  140. Assert.True (buffer.Contents! [0, 0].IsDirty);
  141. Assert.False (buffer.Contents! [0, 1].IsDirty);
  142. Assert.True (buffer.Contents! [0, 2].IsDirty);
  143. // Act
  144. output.Write (buffer);
  145. Assert.Contains ("🦮", output.Output);
  146. Assert.Contains ("A", output.Output);
  147. // Dirty flags cleared for the written cells
  148. Assert.False (buffer.Contents! [0, 0].IsDirty);
  149. Assert.False (buffer.Contents! [0, 1].IsDirty);
  150. Assert.False (buffer.Contents! [0, 2].IsDirty);
  151. Assert.Equal (new (0, 0), output.GetCursorPosition ());
  152. // Now write 'X' at col 1 which replaces with the replacement character the col 0
  153. buffer.Move (1, 0);
  154. buffer.AddStr ("X");
  155. // Confirm dirtiness state before to write
  156. Assert.True (buffer.Contents! [0, 0].IsDirty);
  157. Assert.True (buffer.Contents! [0, 1].IsDirty);
  158. Assert.True (buffer.Contents! [0, 2].IsDirty);
  159. output.Write (buffer);
  160. Assert.Contains ("�", output.Output);
  161. Assert.Contains ("X", output.Output);
  162. // Dirty flags cleared for the written cells
  163. Assert.False (buffer.Contents! [0, 0].IsDirty);
  164. Assert.False (buffer.Contents! [0, 1].IsDirty);
  165. Assert.False (buffer.Contents! [0, 2].IsDirty);
  166. // Verify SetCursorPositionImpl was invoked by WriteToConsole (cursor set to a written column)
  167. Assert.Equal (new (0, 0), output.GetCursorPosition ());
  168. }
  169. [Theory]
  170. [InlineData (true)]
  171. [InlineData (false)]
  172. public void Write_EmitsSixelDataAndPositionsCursor (bool isLegacyConsole)
  173. {
  174. // Arrange
  175. var output = new FakeOutput ();
  176. IOutputBuffer buffer = output.LastBuffer!;
  177. buffer.SetSize (1, 1);
  178. // Ensure the buffer has some content so Write traverses rows
  179. buffer.AddStr (".");
  180. // Create a Sixel to render
  181. var s = new SixelToRender
  182. {
  183. SixelData = "SIXEL-DATA",
  184. ScreenPosition = new Point (4, 2)
  185. };
  186. // Create DriverImpl and associate it with the FakeOutput to test Sixel output
  187. IDriver driver = new DriverImpl (
  188. new FakeInputProcessor (null!),
  189. new OutputBufferImpl (),
  190. output,
  191. new (new AnsiResponseParser ()),
  192. new SizeMonitorImpl (output));
  193. // Add the Sixel to the driver
  194. driver.GetSixels ().Enqueue (s);
  195. // FakeOutput exposes this because it's in test scope
  196. output.IsLegacyConsole = isLegacyConsole;
  197. // Act
  198. output.Write (buffer);
  199. if (!isLegacyConsole)
  200. {
  201. // Assert: Sixel data was emitted (use Contains to avoid equality/side-effects)
  202. Assert.Contains ("SIXEL-DATA", output.Output);
  203. // Cursor was moved to Sixel position
  204. Assert.Equal (s.ScreenPosition, output.GetCursorPosition ());
  205. }
  206. else
  207. {
  208. // Assert: Sixel data was NOT emitted
  209. Assert.DoesNotContain ("SIXEL-DATA", output.Output);
  210. // Cursor was NOT moved to Sixel position
  211. Assert.NotEqual (s.ScreenPosition, output.GetCursorPosition ());
  212. }
  213. IApplication app = Application.Create ();
  214. app.Driver = driver;
  215. Assert.Equal (driver.GetSixels (), app.Driver.GetSixels ());
  216. app.Dispose ();
  217. }
  218. }