using TermRemoteCtl.Agent.Terminal.Screen; namespace TermRemoteCtl.Agent.Tests.Terminal; public sealed class TerminalScreenEngineTests { [Fact] public void ApplyOutput_Clear_Removes_Stale_Lines_From_Visible_Snapshot() { var engine = new TerminalScreenEngine(rows: 4, cols: 20); engine.ApplyOutput("first line\r\nsecond line", sourceSequence: 1); engine.ApplyOutput("\u001b[2J\u001b[Hprompt> ", sourceSequence: 2); var snapshot = engine.CreateSnapshot("session-1"); Assert.Equal(2L, snapshot.SourceSequence); Assert.Equal(2L, snapshot.ScreenVersion); Assert.StartsWith("prompt> ", snapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); Assert.DoesNotContain( snapshot.PrimaryBuffer.Viewport, line => line.Text.Contains("first line", StringComparison.Ordinal)); Assert.Equal(0, snapshot.CursorRow); Assert.Equal(8, snapshot.CursorColumn); } [Fact] public void ApplyOutput_Cursor_Movement_And_Insert_Produces_Final_Visible_Line() { var engine = new TerminalScreenEngine(rows: 4, cols: 40); engine.ApplyOutput("PS> gst", sourceSequence: 1); engine.ApplyOutput("\u001b[2D", sourceSequence: 2); engine.ApplyOutput("it", sourceSequence: 3); var snapshot = engine.CreateSnapshot("session-1"); Assert.StartsWith("PS> git", snapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); Assert.Equal(0, snapshot.CursorRow); Assert.Equal(7, snapshot.CursorColumn); Assert.Equal(3L, snapshot.SourceSequence); Assert.Equal(3L, snapshot.ScreenVersion); } [Fact] public void ApplyOutput_Alternate_Buffer_Preserves_Primary_And_Restores_On_Exit() { var engine = new TerminalScreenEngine(rows: 4, cols: 20); engine.ApplyOutput("primary view", sourceSequence: 1); engine.ApplyOutput("\u001b[?1049halt view", sourceSequence: 2); var alternateSnapshot = engine.CreateSnapshot("session-1"); Assert.Equal("alternate", alternateSnapshot.ActiveBuffer); Assert.StartsWith("alt view", alternateSnapshot.AlternateBuffer!.Viewport[0].Text, StringComparison.Ordinal); Assert.StartsWith("primary view", alternateSnapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); engine.ApplyOutput("\u001b[?1049l", sourceSequence: 3); var restoredSnapshot = engine.CreateSnapshot("session-1"); Assert.Equal("primary", restoredSnapshot.ActiveBuffer); Assert.StartsWith("primary view", restoredSnapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); Assert.StartsWith("alt view", restoredSnapshot.AlternateBuffer!.Viewport[0].Text, StringComparison.Ordinal); } [Theory] [InlineData("\u001b[?1047h", "\u001b[?1047l")] [InlineData("\u001b[?47h", "\u001b[?47l")] public void ApplyOutput_Alternate_Buffer_Compatibility_Modes_Switch_Buffers( string enterSequence, string exitSequence) { var engine = new TerminalScreenEngine(rows: 4, cols: 20); engine.ApplyOutput("primary", sourceSequence: 1); engine.ApplyOutput($"{enterSequence}alt", sourceSequence: 2); var alternateSnapshot = engine.CreateSnapshot("session-1"); Assert.Equal("alternate", alternateSnapshot.ActiveBuffer); Assert.StartsWith("alt", alternateSnapshot.AlternateBuffer!.Viewport[0].Text, StringComparison.Ordinal); Assert.StartsWith("primary", alternateSnapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); engine.ApplyOutput(exitSequence, sourceSequence: 3); var restoredSnapshot = engine.CreateSnapshot("session-1"); Assert.Equal("primary", restoredSnapshot.ActiveBuffer); Assert.StartsWith("primary", restoredSnapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); } [Fact] public void ApplyOutput_Osc_Window_Title_Does_Not_Leak_Into_Visible_Screen() { var engine = new TerminalScreenEngine(rows: 4, cols: 80); engine.ApplyOutput("\u001b]0;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\powershell.exe\u0007", sourceSequence: 1); engine.ApplyOutput("PS D:\\App\\python\\MLplatform> pwd", sourceSequence: 2); var snapshot = engine.CreateSnapshot("session-1"); Assert.DoesNotContain( snapshot.PrimaryBuffer.Viewport, line => line.Text.Contains("]0;", StringComparison.Ordinal)); Assert.DoesNotContain( snapshot.PrimaryBuffer.Viewport, line => line.Text.Contains("powershell.exe", StringComparison.OrdinalIgnoreCase)); Assert.StartsWith( "PS D:\\App\\python\\MLplatform> pwd", snapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); Assert.Equal(0, snapshot.CursorRow); Assert.Equal(32, snapshot.CursorColumn); } [Fact] public void ApplyOutput_Osc_Window_Title_With_String_Terminator_Does_Not_Leak_Into_Visible_Screen() { var engine = new TerminalScreenEngine(rows: 4, cols: 80); engine.ApplyOutput("\u001b]0;PowerShell Title\u001b\\", sourceSequence: 1); engine.ApplyOutput("prompt> ", sourceSequence: 2); var snapshot = engine.CreateSnapshot("session-1"); Assert.DoesNotContain( snapshot.PrimaryBuffer.Viewport, line => line.Text.Contains("PowerShell Title", StringComparison.Ordinal)); Assert.StartsWith("prompt> ", snapshot.PrimaryBuffer.Viewport[0].Text, StringComparison.Ordinal); Assert.Equal(0, snapshot.CursorRow); Assert.Equal(8, snapshot.CursorColumn); } }