diff --git a/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs index 624213eb4..b8de3d9ec 100644 --- a/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs +++ b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs @@ -3,7 +3,7 @@ namespace Terminal.Gui; // Enum to manage the parser's state - internal enum ParserState + public enum ParserState { Normal, ExpectingBracket, diff --git a/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs b/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs index 6535c978d..b80be5ae3 100644 --- a/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs +++ b/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System.CommandLine.Parsing; +using System.Diagnostics; using System.Text; using Xunit.Abstractions; @@ -103,7 +104,6 @@ public class AnsiResponseParserTests (ITestOutputHelper output) // Add more test cases here... public void TestInputSequences (string ansiStream, string expectedTerminator, string expectedResponse, string expectedOutput) { - var swGenBatches = Stopwatch.StartNew (); int tests = 0; @@ -146,6 +146,111 @@ public class AnsiResponseParserTests (ITestOutputHelper output) output.WriteLine ($"Tested {tests} in {swRunTest.ElapsedMilliseconds} ms (gen batches took {swGenBatches.ElapsedMilliseconds} ms)" ); } + public static IEnumerable TestInputSequencesExact_Cases () + { + yield return + [ + "Esc Only", + null, + new [] + { + new StepExpectation ('\u001b',ParserState.ExpectingBracket,string.Empty) + } + ]; + + yield return + [ + "Esc Hi with intermediate", + 'c', + new [] + { + new StepExpectation ('\u001b',ParserState.ExpectingBracket,string.Empty), + new StepExpectation ('H',ParserState.Normal,"\u001bH"), // H is known terminator and not expected one so here we release both chars + new StepExpectation ('\u001b',ParserState.ExpectingBracket,string.Empty), + new StepExpectation ('[',ParserState.InResponse,string.Empty), + new StepExpectation ('0',ParserState.InResponse,string.Empty), + new StepExpectation ('c',ParserState.Normal,string.Empty,"\u001b[0c"), // c is expected terminator so here we swallow input and populate expected response + new StepExpectation ('\u001b',ParserState.ExpectingBracket,string.Empty), + } + ]; + } + + public class StepExpectation () + { + /// + /// The input character to feed into the parser at this step of the test + /// + public char Input { get; } + + /// + /// What should the state of the parser be after the + /// is fed in. + /// + public ParserState ExpectedStateAfterOperation { get; } + + /// + /// If this step should release one or more characters, put them here. + /// + public string ExpectedRelease { get; } = string.Empty; + + /// + /// If this step should result in a completing of detection of ANSI response + /// then put the expected full response sequence here. + /// + public string ExpectedAnsiResponse { get; } = string.Empty; + + public StepExpectation ( + char input, + ParserState expectedStateAfterOperation, + string expectedRelease = "", + string expectedAnsiResponse = "") : this () + { + Input = input; + ExpectedStateAfterOperation = expectedStateAfterOperation; + ExpectedRelease = expectedRelease; + ExpectedAnsiResponse = expectedAnsiResponse; + } + + } + + + + [MemberData(nameof(TestInputSequencesExact_Cases))] + [Theory] + public void TestInputSequencesExact (string caseName, char? terminator, IEnumerable expectedStates) + { + output.WriteLine ("Running test case:" + caseName); + + var parser = new AnsiResponseParser (); + string? response = null; + + if (terminator.HasValue) + { + parser.ExpectResponse (terminator.Value.ToString (),(s)=> response = s); + } + foreach (var state in expectedStates) + { + // If we expect the response to be detected at this step + if (!string.IsNullOrWhiteSpace (state.ExpectedAnsiResponse)) + { + // Then before passing input it should be null + Assert.Null (response); + } + + var actual = parser.ProcessInput (state.Input.ToString ()); + + Assert.Equal (state.ExpectedRelease,actual); + Assert.Equal (state.ExpectedStateAfterOperation, parser.State); + + // If we expect the response to be detected at this step + if (!string.IsNullOrWhiteSpace (state.ExpectedAnsiResponse)) + { + // And after passing input it shuld be the expected value + Assert.Equal (state.ExpectedAnsiResponse, response); + } + } + } + [Fact] public void ReleasesEscapeAfterTimeout () {