From 883ee600fa35ae333abe037e5950f221ae79f13b Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 24 Jul 2023 22:56:10 -0400 Subject: [PATCH] Fix AsyncStreamReader sending extra string and tests --- .../Processes/AsyncStreamReader.cs | 3 + .../Core/AsyncStreamReaderTests.cs | 74 ++++++++++--------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/StabilityMatrix.Core/Processes/AsyncStreamReader.cs b/StabilityMatrix.Core/Processes/AsyncStreamReader.cs index 798402c4..f42d0f2e 100644 --- a/StabilityMatrix.Core/Processes/AsyncStreamReader.cs +++ b/StabilityMatrix.Core/Processes/AsyncStreamReader.cs @@ -254,6 +254,9 @@ internal sealed class AsyncStreamReader : IDisposable else { // Send buffer up to this point, not including \r + // But skip if there's no content + if (currentIndex == lineStart) break; + var line = _sb.ToString(lineStart, currentIndex - lineStart); lock (_messageQueue) { diff --git a/StabilityMatrix.Tests/Core/AsyncStreamReaderTests.cs b/StabilityMatrix.Tests/Core/AsyncStreamReaderTests.cs index a04b58e3..ebf0e8e8 100644 --- a/StabilityMatrix.Tests/Core/AsyncStreamReaderTests.cs +++ b/StabilityMatrix.Tests/Core/AsyncStreamReaderTests.cs @@ -13,62 +13,68 @@ public class AsyncStreamReaderTests [DataTestMethod] // Test newlines handling for \r\n, \n - [DataRow("a\r\nb\nc", "a\r\n", "b\n", "c")] + [DataRow("a\r\nb\nc", "a\r\n", "b\n", "c", null)] // Carriage returns \r should be sent as is - [DataRow("a\rb\rc", "a", "\rb", "\rc")] - [DataRow("a1\ra2\nb1\rb2", "a1", "\ra2\n", "b1", "\rb2")] + [DataRow("a\rb\rc", "a", "\rb", "\rc", null)] + [DataRow("a1\ra2\nb1\rb2", "a1", "\ra2\n", "b1", "\rb2", null)] // Ansi escapes should be seperated - [DataRow("\x1b[A\x1b[A", "\x1b[A", "\x1b[A")] + [DataRow("\x1b[A\x1b[A", "\x1b[A", "\x1b[A", null)] // Mixed Ansi and newlines - [DataRow("a \x1b[A\r\n\r xyz", "a ", "\x1b[A", "\r\n", "\r xyz")] - public async Task TestRead(string source, params string[] expected) + [DataRow("a \x1b[A\r\n\r xyz", "a ", "\x1b[A", "\r\n", "\r xyz", null)] + public async Task TestRead(string source, params string?[] expected) { - var queue = new Queue(expected); + var results = new List(); var callback = new Action(s => { - Assert.IsTrue(queue.Count > 0); - Assert.AreEqual(queue.Dequeue(), s); + results.Add(s); }); - var stream = new MemoryStream(Encoding.UTF8.GetBytes(source)); - - // Make the reader - using var reader = new AsyncStreamReader(stream, callback, Encoding.UTF8); - - // Begin read line and wait until finish - reader.BeginReadLine(); - await reader.EOF; - - // Check if all expected strings were read - Assert.AreEqual(0, queue.Count, "Remaining: " + string.Join(", ", queue.ToArray() - .Select(s => (s ?? "").ToRepr()))); + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(source)); + using (var reader = new AsyncStreamReader(stream, callback, Encoding.UTF8)) + { + // Begin read line and wait until finish + reader.BeginReadLine(); + // Wait for maximum 1 second + await reader.EOF.WaitAsync(new CancellationTokenSource(1000).Token); + } + + // Check expected output matches + Assert.IsTrue(expected.SequenceEqual(results.ToArray()), + "Results [{0}] do not match expected [{1}]", + string.Join(", ", results.Select(s => s?.ToRepr() ?? "")), + string.Join(", ", expected.Select(s => s?.ToRepr() ?? ""))); } [TestMethod] public async Task TestCarriageReturnHandling() { - // The previous buffer should be sent when \r is encountered - const string source = "dog\r\ncat\r123\r456"; - var stream = new MemoryStream(Encoding.UTF8.GetBytes(source)); + var expected = new[] {"dog\r\n", "cat", "\r123", "\r456", null}; - var queue = new Queue(new[] {"dog\r\n", "cat", "\r123", "\r456"}); + var results = new List(); var callback = new Action(s => { - Assert.IsTrue(queue.Count > 0); - Assert.AreEqual(queue.Dequeue(), s); + results.Add(s); }); - // Make the reader - using var reader = new AsyncStreamReader(stream, callback, Encoding.UTF8); + // The previous buffer should be sent when \r is encountered + const string source = "dog\r\ncat\r123\r456"; - // Begin read line and wait until finish - reader.BeginReadLine(); - await reader.EOF; + // Make the reader + using var stream = new MemoryStream(Encoding.UTF8.GetBytes(source)); + using (var reader = new AsyncStreamReader(stream, callback, Encoding.UTF8)) + { + // Begin read line and wait until finish + reader.BeginReadLine(); + // Wait for maximum 1 second + await reader.EOF.WaitAsync(new CancellationTokenSource(1000).Token); + } // Check if all expected strings were read - Assert.AreEqual(0, queue.Count, "Remaining: " + string.Join(", ", queue.ToArray() - .Select(s => (s ?? "").ToRepr()))); + Assert.IsTrue(expected.SequenceEqual(results.ToArray()), + "Results [{0}] do not match expected [{1}]", + string.Join(", ", results.Select(s => s?.ToRepr() ?? "")), + string.Join(", ", expected.Select(s => s?.ToRepr() ?? ""))); } }