using System.Text; namespace DodoSSH.Client.Terminal.Tests; /// /// That a session keeps the last of its output for the phone's history to read back, and knows when a /// full-screen program has it. /// public sealed class OutputTailTests { private static byte[] Ascii(string text) => Encoding.ASCII.GetBytes(text); [Fact] public void WhatIsHeld_IsTheLastKilobyte_InOrder() { var tail = new OutputTail(); tail.Append(Ascii(new string('a', OutputTail.Capacity))); tail.Append(Ascii("stan@box:~$ ls")); var held = Encoding.ASCII.GetString(tail.Snapshot().Tail); held.Length.ShouldBe(OutputTail.Capacity); held.ShouldEndWith("aaastan@box:~$ ls"); } [Fact] public void AReadLargerThanTheTail_KeepsItsEnd() { var tail = new OutputTail(); tail.Append(Ascii(new string('x', 5000) + "the end")); Encoding.ASCII.GetString(tail.Snapshot().Tail).ShouldEndWith("xthe end"); } [Fact] public void TheAlternateScreen_IsOnBetweenItsTwoSwitches() { var tail = new OutputTail(); tail.Append(Ascii("$ vi notes\r\n\x1b[?1049h\x1b[H~\r\n~")); tail.Snapshot().AlternateScreen.ShouldBeTrue(); tail.Append(Ascii("\x1b[?1049l$ ")); tail.Snapshot().AlternateScreen.ShouldBeFalse(); } [Fact] public void ASwitchSplitAcrossTwoReads_IsStillSeen() { var tail = new OutputTail(); tail.Append(Ascii("$ less log\r\n\x1b[?10")); tail.Append(Ascii("49h")); tail.Snapshot().AlternateScreen.ShouldBeTrue(); } [Fact] public void ASwitchAtTheStartOfAHugeRead_IsNotLostWithTheBytesThatFallOffTheTail() { var tail = new OutputTail(); // A full-screen program's first frame: the switch, and then a screenful of paint. tail.Append(Ascii("\x1b[?1049h" + new string('#', 60_000))); tail.Snapshot().AlternateScreen.ShouldBeTrue(); } [Fact] public void OtherPrivateModes_AreNotTheAlternateScreen() { var tail = new OutputTail(); // Bracketed paste and cursor visibility, which every prompt sends. tail.Append(Ascii("\x1b[?2004h\x1b[?25h$ ")); tail.Snapshot().AlternateScreen.ShouldBeFalse(); } }