From ac09a6add486562205ad812eef36a9f1aa97d1a2 Mon Sep 17 00:00:00 2001 From: tznind Date: Fri, 11 Oct 2024 13:30:53 +0100 Subject: [PATCH] Initial test to define the task of the parser --- .../ConsoleDrivers/AnsiResponseParser.cs | 20 +++++ .../ConsoleDrivers/AnsiResponseParserTests.cs | 76 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs create mode 100644 UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs diff --git a/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs new file mode 100644 index 000000000..fd4218bdd --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs @@ -0,0 +1,20 @@ +#nullable enable + +namespace Terminal.Gui; +class AnsiResponseParser +{ + + public bool ConsumeInput (char character, out string? released) + { + // if character is escape + + // start consuming till we see terminator + + released = null; + return false; + } + + public void ExpectResponse (char terminator, Action response) + { + } +} diff --git a/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs b/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs new file mode 100644 index 000000000..79f0bbba4 --- /dev/null +++ b/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs @@ -0,0 +1,76 @@ +namespace UnitTests.ConsoleDrivers; +public class AnsiResponseParserTests +{ + AnsiResponseParser _parser = new AnsiResponseParser (); + + [Fact] + public void TestInputProcessing () + { + string ansiStream = "\x1B[<0;10;20M" + // ANSI escape for mouse move at (10, 20) + "Hello" + // User types "Hello" + "\x1B[0c"; // Device Attributes response (e.g., terminal identification i.e. DAR) + + + string? response = null; + + int i = 0; + + // Imagine that we are expecting a DAR + _parser.ExpectResponse ('c',(s)=> response = s); + + // First char is Escape which we must consume incase what follows is the DAR + AssertConsumed (ansiStream, ref i); // Esc + + for (int c = 0; c < "[<0;10;20".Length; c++) + { + AssertConsumed (ansiStream, ref i); + } + + // We see the M terminator + AssertReleased (ansiStream, ref i, "\x1B[<0;10;20M"); + + // Regular user typing + for (int c = 0; c < "Hello".Length; c++) + { + AssertIgnored (ansiStream, ref i); + } + + // Now we have entered the actual DAR we should be consuming these + for (int c = 0; c < "\x1B [0".Length; c++) + { + AssertConsumed (ansiStream, ref i); + } + + // Consume the terminator 'c' and expect this to call the above event + Assert.Null (response); + AssertConsumed (ansiStream, ref i); + Assert.NotNull (response); + Assert.Equal ("\u001b[0c", response); + } + + private void AssertIgnored (string ansiStream, ref int i) + { + // Parser does not grab this key (i.e. driver can continue with regular operations) + Assert.False (_parser.ConsumeInput (NextChar (ansiStream, ref i), out var released)); + Assert.Null (released); + } + private void AssertConsumed (string ansiStream, ref int i) + { + // Parser grabs this key + Assert.True (_parser.ConsumeInput( NextChar (ansiStream, ref i), out var released)); + Assert.Null (released); + } + private void AssertReleased (string ansiStream, ref int i, string expectedRelease) + { + // Parser realizes it has grabbed content that does not belong to an outstanding request + // Parser returns false to indicate to continue + Assert.False(_parser.ConsumeInput (NextChar (ansiStream,ref i), out var released)); + + // Parser releases all the grabbed content back to the driver + Assert.Equal ( released,expectedRelease); + } + private char NextChar (string ansiStream, ref int i) + { + return ansiStream [i++]; + } +}