From e5b9959d8e79171df1f11114393867d4892a6072 Mon Sep 17 00:00:00 2001 From: Andreas Mueller Date: Sun, 19 Dec 2021 22:21:05 +0100 Subject: [PATCH] StreamExtensions mit UnitTests versehen --- .../Extensions/StreamExtensionsTests.cs | 138 ++++++++++++++++++ AMWD.Common/Extensions/StreamExtensions.cs | 47 ++++-- CHANGELOG.md | 2 + 3 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 AMWD.Common.Tests/Extensions/StreamExtensionsTests.cs diff --git a/AMWD.Common.Tests/Extensions/StreamExtensionsTests.cs b/AMWD.Common.Tests/Extensions/StreamExtensionsTests.cs new file mode 100644 index 0000000..731420c --- /dev/null +++ b/AMWD.Common.Tests/Extensions/StreamExtensionsTests.cs @@ -0,0 +1,138 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AMWD.Common.Tests.Extensions +{ + [TestClass] + public class StreamExtensionsTests + { + [TestMethod] + public void ShouldReadLineFromStreamSynchronous() + { + // arrange + var sb = new StringBuilder(); + sb.AppendLine("First Line"); + sb.AppendLine("Second Line"); + byte[] buffer = Encoding.UTF8.GetBytes(sb.ToString().Trim()); + var stream = new MemoryStream(buffer); + + // act + string line = stream.ReadLine(); + + // assert + Assert.AreEqual("First Line", line); + + stream.Dispose(); + } + + [TestMethod] + public void ShouldReadUntilEndAsLineSynchronous() + { + // arrange + byte[] buffer = Encoding.UTF8.GetBytes("Single Line"); + var stream = new MemoryStream(buffer); + + // act + string line = stream.ReadLine(); + + // assert + Assert.AreEqual("Single Line", line); + + stream.Dispose(); + } + + [TestMethod] + public void ShouldReturnNullWhenNotReadableSynchronous() + { + // arrange + var stream = new WriteOnlyStream(); + + // act + string line = stream.ReadLine(); + + // assert + Assert.IsNull(line); + + stream.Dispose(); + } + + [TestMethod] + public async Task ShouldReadLineFromStreamAsynchronous() + { + // arrange + var sb = new StringBuilder(); + sb.AppendLine("First Line"); + sb.AppendLine("Second Line"); + byte[] buffer = Encoding.UTF8.GetBytes(sb.ToString().Trim()); + var stream = new MemoryStream(buffer); + + // act + string line = await stream.ReadLineAsync(); + + // assert + Assert.AreEqual("First Line", line); + + stream.Dispose(); + } + + [TestMethod] + public async Task ShouldReadUntilEndAsLineAsynchronous() + { + // arrange + byte[] buffer = Encoding.UTF8.GetBytes("Single Line"); + var stream = new MemoryStream(buffer); + + // act + string line = await stream.ReadLineAsync(); + + // assert + Assert.AreEqual("Single Line", line); + + stream.Dispose(); + } + + [TestMethod] + public async Task ShouldReturnNullWhenNotReadableAsynchronous() + { + // arrange + var stream = new WriteOnlyStream(); + + // act + string line = await stream.ReadLineAsync(); + + // assert + Assert.IsNull(line); + + stream.Dispose(); + } + + private class WriteOnlyStream : Stream + { + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length => 0; + + public override long Position { get => 0; set => throw new NotImplementedException(); } + + public override void Flush() + { } + + public override int Read(byte[] buffer, int offset, int count) => 0; + + public override long Seek(long offset, SeekOrigin origin) => 0; + + public override void SetLength(long value) + { } + + public override void Write(byte[] buffer, int offset, int count) + { } + } + } +} diff --git a/AMWD.Common/Extensions/StreamExtensions.cs b/AMWD.Common/Extensions/StreamExtensions.cs index a83d825..0b8c0a2 100644 --- a/AMWD.Common/Extensions/StreamExtensions.cs +++ b/AMWD.Common/Extensions/StreamExtensions.cs @@ -1,27 +1,36 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -namespace AMWD.Common.Extensions +namespace System.IO { + /// + /// Provides extension methods for the . + /// public static class StreamExtensions { - public static string ReadLine(this Stream stream, Encoding encoding = null, string eol = null) + /// + /// Reads a line from a . + /// + /// The stream to read from. + /// The encoding to use. + /// The character determinating a line end. + /// + public static string ReadLine(this Stream stream, Encoding encoding = null, char? eol = null) { if (encoding == null) encoding = Encoding.Default; if (eol == null) - eol = Environment.NewLine; + eol = Environment.NewLine.Last(); if (!stream.CanRead) return null; var bytes = new List(); - string s; + char ch; do { int result = stream.ReadByte(); @@ -30,37 +39,45 @@ namespace AMWD.Common.Extensions byte b = (byte)result; bytes.Add(b); - s = encoding.GetString(new[] { b }); + ch = (char)result; } - while (!eol.Contains(s)); + while (ch != eol); return encoding.GetString(bytes.ToArray()).Trim(); } - public static async Task ReadLineAsync(this Stream stream, Encoding encoding = null, string eol = null, CancellationToken cancellationToken = default) + /// + /// Reads a line from a asynchronous. + /// + /// The stream to read from. + /// The encoding to use. + /// The character determinating a line end. + /// The token to monitor for cancellation requests. The default value is . + /// + public static async Task ReadLineAsync(this Stream stream, Encoding encoding = null, char? eol = null, CancellationToken cancellationToken = default) { if (encoding == null) encoding = Encoding.Default; if (eol == null) - eol = Environment.NewLine; + eol = Environment.NewLine.Last(); if (!stream.CanRead) return null; var bytes = new List(); - string s; + char ch; do { byte[] buffer = new byte[1]; - int count = await stream.ReadAsync(buffer, 0, buffer.Length); + int count = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); if (count == 0) break; bytes.Add(buffer[0]); - s = encoding.GetString(buffer); + ch = (char)buffer[0]; } - while (!eol.Contains(s)); + while (ch != eol); return encoding.GetString(bytes.ToArray()).Trim(); } diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d04221..6842b7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - CHANGELOG - `HtmlHelper.IsDarkColor` to classify a color as dark or light one (by luminance) +- `ReadLine` and `ReadLineAsync` as `StreamExtensions` + ### Changed - Unit-Tests enhanced