using System; using System.Collections.Generic; using System.IO; using System.Linq; using Developpez.Dotnet.Collections; using Developpez.Dotnet.IO; using NUnit.Framework; namespace Developpez.Dotnet.Tests.IO { [TestFixture] public class StreamExtensionsTests { private static readonly Random _random = new Random(); #if !DOTNET4 [Test] public void Check_CopyTo() { byte[] data = GetRandomBytes(10000); using(MemoryStream input = new MemoryStream(data)) using (MemoryStream output = new MemoryStream()) { input.CopyTo(output); byte[] copy = output.ToArray(); CollectionAssert.AreEqual(data, copy); } } #endif [Test] public void Check_CopyToFile() { string path = Path.GetTempFileName(); byte[] data = GetRandomBytes(10000); using (MemoryStream input = new MemoryStream(data)) { input.CopyToFile(path); byte[] copy = File.ReadAllBytes(path); CollectionAssert.AreEqual(data, copy); } } [Test] public void Test_AsNonClosing() { string expected = "Hello world !"; using (MemoryStream ms = new MemoryStream()) { using (StreamWriter wr = new StreamWriter(ms.AsNonClosing())) { wr.WriteLine(expected); } try { // Planterait sans le AsNonClosing ms.Position = 0; using (StreamReader rd = new StreamReader(ms.AsNonClosing())) { string actual = rd.ReadLine(); Assert.AreEqual(expected, actual); } // Planterait sans le AsNonClosing ms.Position = 0; } catch(ObjectDisposedException) { Assert.Fail("Le stream a été disposé prématurément"); } } } [Test] public void Test_Tee_ArgumentValidation() { var nullStream = default(Stream); var actions = new TestDelegate[] { () => nullStream.Tee(Stream.Null, Stream.Null), () => Stream.Null.Tee(null, Stream.Null), () => Stream.Null.Tee(Stream.Null, null), }; foreach (var action in actions) { Assert.Throws(action); } var readOnly = new DummyReadOnlyStream(); actions = new TestDelegate[] { () => readOnly.Tee(Stream.Null, Stream.Null), () => Stream.Null.Tee(readOnly, Stream.Null), () => Stream.Null.Tee(Stream.Null, readOnly) }; foreach (var action in actions) { Assert.Throws(action); } } [Test] public void Test_Tee() { using (var m1 = new MemoryStream()) using (var m2 = new MemoryStream()) { using (var tee = m1.Tee(m2)) { var bytes = new byte[50]; new Random().NextBytes(bytes); tee.Write(bytes, 0, bytes.Length); Assert.AreEqual(50, m1.Length); CollectionAssert.AreEqual(m1.ToArray(), m2.ToArray()); } Assert.Throws(() => m1.Position = 0); Assert.Throws(() => m2.Position = 0); } } private class DummyReadOnlyStream : Stream { #region Overrides of Stream public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { return 0; } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return false; } } public override long Length { get { throw new NotSupportedException(); } } public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } #endregion } [Test] public void Test_AsByteEnumerable() { byte[] data = new byte[10000]; Random rnd = new Random(); rnd.NextBytes(data); using (MemoryStream input = new MemoryStream(data)) { int i = 0; foreach (var b in input.AsByteEnumerable()) { Assert.AreEqual(data[i], b); i++; } } } [Test] public void Test_AsBlockEnumerable() { byte[] data = GetRandomBytes(10000); using (MemoryStream input = new MemoryStream(data)) { int blockSize = 512; int p = 0; foreach (var block in input.AsBlockEnumerable(blockSize)) { CollectionAssert.AreEqual(data.Skip(p).Take(blockSize), block); p += blockSize; } } } [Test] public void Test_ReadAllBytes() { byte[] data = GetRandomBytes(10000); using (MemoryStream ms = new MemoryStream(data)) { byte[] copy = ms.ReadAllBytes(); CollectionAssert.AreEqual(data, copy); } } [Test] public void Test_ReadBytes() { byte[] data = GetRandomBytes(10000); using (var ms = new MemoryStream(data)) { byte[] first10 = ms.ReadBytes(10); CollectionAssert.AreEqual(data.Take(10), first10); } } [Test] public void Test_ReadBytes_WithIrregularStream() { var blocks = new[] { new byte[] { 1, 2, 3, 0, 4 }, new byte[] { 9, 8, 7 }, new byte[] { 0, 0, 0, 0, 0 }, }; var data = blocks.SelectMany(b => b).ToArray(); using (var stream = new BlockStream(blocks)) { byte[] first3 = stream.ReadBytes(3); CollectionAssert.AreEqual(data.Take(3), first3); stream.Position = 0; byte[] first10 = stream.ReadBytes(10); CollectionAssert.AreEqual(data.Take(10), first10); stream.Position = 0; byte[] first20 = stream.ReadBytes(20); CollectionAssert.AreEqual(data.Take(20), first20); } } #region Test helpers private static byte[] GetRandomBytes(int count) { byte[] data = new byte[10000]; _random.NextBytes(data); return data; } class BlockStream : Stream { private readonly byte[][] _blocks; private readonly int _length; private long _position = 0; private int _currentBlock; private int _offset; public BlockStream(IEnumerable blocks) { _blocks = blocks.ToArray(); _length = _blocks.Sum(b => b.Length); _currentBlock = 0; _offset = 0; } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override bool CanWrite { get { return false; } } public override void Flush() { } public override long Length { get { return _length; } } public override long Position { get { return _position; } set { Seek(value, SeekOrigin.Begin); } } public override int Read(byte[] buffer, int offset, int count) { if (_position >= _length) return 0; var block = _blocks[_currentBlock]; int bytesToReturn = Math.Min(count, block.Length - _offset); Array.Copy(block, _offset, buffer, offset, bytesToReturn); _offset += bytesToReturn; if (_offset >= block.Length) { _currentBlock++; _offset = 0; } _position += bytesToReturn; return bytesToReturn; } public override long Seek(long offset, SeekOrigin origin) { long newPos; switch (origin) { case SeekOrigin.Begin: offset.CheckArgumentOutOfRange("offset", 0, _length - 1); newPos = offset; break; case SeekOrigin.Current: offset.CheckArgumentOutOfRange("offset", -_position, _length - _position - 1); newPos = _position + offset; break; case SeekOrigin.End: offset.CheckArgumentOutOfRange("offset", -_length + 1, 0); newPos = _length + offset; break; default: throw new ArgumentOutOfRangeException("origin"); } long total = 0; _currentBlock = _blocks.IndexOf(b => { if (total <= newPos && total + b.Length > newPos) return true; total += b.Length; return false; }); _offset = (int) (newPos - total); _position = newPos; return _position; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } #endregion } }