#region CPL License /* Nuclex Framework Copyright (C) 2002-2017 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion #if UNITTEST using System; using System.IO; using NUnit.Framework; namespace Nuclex.Support.IO { /// Unit Test for the partial stream [TestFixture] internal class PartialStreamTest { #region class TestStream /// Testing stream that allows specific features to be disabled private class TestStream : Stream { /// Initializes a new test stream /// Stream that will be wrapped /// Whether to allow reading from the stream /// Whether to allow writing to the stream /// Whether to allow seeking within the stream public TestStream( Stream wrappedStream, bool allowRead, bool allowWrite, bool allowSeek ) { this.stream = wrappedStream; this.readAllowed = allowRead; this.writeAllowed = allowWrite; this.seekAllowed = allowSeek; } /// Whether data can be read from the stream public override bool CanRead { get { return this.readAllowed; } } /// Whether the stream supports seeking public override bool CanSeek { get { return this.seekAllowed; } } /// Whether data can be written into the stream public override bool CanWrite { get { return this.writeAllowed; } } /// /// Clears all buffers for this stream and causes any buffered data to be written /// to the underlying device. /// public override void Flush() { ++this.flushCallCount; this.stream.Flush(); } /// Length of the stream in bytes public override long Length { get { enforceSeekAllowed(); return this.stream.Length; } } /// Absolute position of the file pointer within the stream /// /// At least one of the chained streams does not support seeking /// public override long Position { get { enforceSeekAllowed(); return this.stream.Position; } set { enforceSeekAllowed(); this.stream.Position = value; } } /// /// Reads a sequence of bytes from the stream and advances the position of /// the file pointer by the number of bytes read. /// /// Buffer that will receive the data read from the stream /// /// Offset in the buffer at which the stream will place the data read /// /// Maximum number of bytes that will be read /// /// The number of bytes that were actually read from the stream and written into /// the provided buffer /// public override int Read(byte[] buffer, int offset, int count) { enforceReadAllowed(); return this.stream.Read(buffer, offset, count); } /// Changes the position of the file pointer /// /// Offset to move the file pointer by, relative to the position indicated by /// the parameter. /// /// /// Reference point relative to which the file pointer is placed /// /// The new absolute position within the stream public override long Seek(long offset, SeekOrigin origin) { enforceSeekAllowed(); return this.stream.Seek(offset, origin); } /// Changes the length of the stream /// New length the stream shall have public override void SetLength(long value) { enforceSeekAllowed(); this.stream.SetLength(value); } /// /// Writes a sequence of bytes to the stream and advances the position of /// the file pointer by the number of bytes written. /// /// /// Buffer containing the data that will be written to the stream /// /// /// Offset in the buffer at which the data to be written starts /// /// Number of bytes that will be written into the stream public override void Write(byte[] buffer, int offset, int count) { enforceWriteAllowed(); this.stream.Write(buffer, offset, count); } /// Number of times the Flush() method has been called public int FlushCallCount { get { return this.flushCallCount; } } /// Throws an exception if reading is not allowed private void enforceReadAllowed() { if(!this.readAllowed) { throw new NotSupportedException("Reading has been disabled"); } } /// Throws an exception if writing is not allowed private void enforceWriteAllowed() { if(!this.writeAllowed) { throw new NotSupportedException("Writing has been disabled"); } } /// Throws an exception if seeking is not allowed private void enforceSeekAllowed() { if(!this.seekAllowed) { throw new NotSupportedException("Seeking has been disabled"); } } /// Stream being wrapped for testing private Stream stream; /// whether to allow reading from the wrapped stream private bool readAllowed; /// Whether to allow writing to the wrapped stream private bool writeAllowed; /// Whether to allow seeking within the wrapped stream private bool seekAllowed; /// Number of times the Flush() method has been called private int flushCallCount; } #endregion // class TestStream /// Tests whether the partial stream constructor is working [Test] public void TestConstructor() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); PartialStream partialStream = new PartialStream(memoryStream, 23, 100); Assert.AreEqual(100, partialStream.Length); } } /// /// Verifies that the partial stream constructor throws an exception if /// it's invoked with an invalid start offset /// [Test] public void TestThrowOnInvalidStartInConstructor() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); Assert.Throws( delegate() { Console.WriteLine(new PartialStream(memoryStream, -1, 10)); } ); } } /// /// Verifies that the partial stream constructor throws an exception if /// it's invoked with an invalid start offset /// [Test] public void TestThrowOnInvalidLengthInConstructor() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); Assert.Throws( delegate() { Console.WriteLine(new PartialStream(memoryStream, 100, 24)); } ); } } /// /// Verifies that the partial stream constructor throws an exception if /// it's invoked with a start offset on an unseekable stream /// [Test] public void TestThrowOnUnseekableStreamWithOffsetInConstructor() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); TestStream testStream = new TestStream(memoryStream, true, true, false); Assert.Throws( delegate() { Console.WriteLine(new PartialStream(testStream, 23, 100)); } ); } } /// /// Tests whether the CanRead property reports its status correctly /// [Test] public void TestCanReadProperty() { using(MemoryStream memoryStream = new MemoryStream()) { TestStream yesStream = new TestStream(memoryStream, true, true, true); TestStream noStream = new TestStream(memoryStream, false, true, true); Assert.IsTrue(new PartialStream(yesStream, 0, 0).CanRead); Assert.IsFalse(new PartialStream(noStream, 0, 0).CanRead); } } /// /// Tests whether the CanWrite property reports its status correctly /// [Test] public void TestCanWriteProperty() { using(MemoryStream memoryStream = new MemoryStream()) { TestStream yesStream = new TestStream(memoryStream, true, true, true); TestStream noStream = new TestStream(memoryStream, true, false, true); Assert.IsTrue(new PartialStream(yesStream, 0, 0).CanWrite); Assert.IsFalse(new PartialStream(noStream, 0, 0).CanWrite); } } /// /// Tests whether the CanSeek property reports its status correctly /// [Test] public void TestCanSeekProperty() { using(MemoryStream memoryStream = new MemoryStream()) { TestStream yesStream = new TestStream(memoryStream, true, true, true); TestStream noStream = new TestStream(memoryStream, true, true, false); Assert.IsTrue(new PartialStream(yesStream, 0, 0).CanSeek); Assert.IsFalse(new PartialStream(noStream, 0, 0).CanSeek); } } /// /// Tests whether the CompleteStream property returns the original stream /// [Test] public void TestCompleteStreamProperty() { using(MemoryStream memoryStream = new MemoryStream()) { PartialStream partialStream = new PartialStream(memoryStream, 0, 0); Assert.AreSame(memoryStream, partialStream.CompleteStream); } } /// Tests whether the Flush() method can be called [Test] public void TestFlush() { using(MemoryStream memoryStream = new MemoryStream()) { PartialStream partialStream = new PartialStream(memoryStream, 0, 0); partialStream.Flush(); } } /// /// Tests whether the Position property correctly reports the file pointer position /// [Test] public void TestGetPosition() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); PartialStream partialStream = new PartialStream(memoryStream, 23, 100); Assert.AreEqual(0, partialStream.Position); byte[] test = new byte[10]; int bytesRead = partialStream.Read(test, 0, 10); Assert.AreEqual(10, bytesRead); Assert.AreEqual(10, partialStream.Position); bytesRead = partialStream.Read(test, 0, 10); Assert.AreEqual(10, bytesRead); Assert.AreEqual(20, partialStream.Position); } } /// /// Tests whether the Position property is correctly updated /// [Test] public void TestSetPosition() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); PartialStream partialStream = new PartialStream(memoryStream, 23, 100); Assert.AreEqual(0, partialStream.Position); partialStream.Position = 7; Assert.AreEqual(partialStream.Position, 7); partialStream.Position = 14; Assert.AreEqual(partialStream.Position, 14); } } /// /// Tests whether the Position property throws an exception if the stream does /// not support seeking. /// [Test] public void TestThrowOnGetPositionOnUnseekableStream() { using(MemoryStream memoryStream = new MemoryStream()) { TestStream testStream = new TestStream(memoryStream, true, true, false); PartialStream partialStream = new PartialStream(testStream, 0, 0); Assert.Throws( delegate() { Console.WriteLine(partialStream.Position); } ); } } /// /// Tests whether the Position property throws an exception if the stream does /// not support seeking. /// [Test] public void TestThrowOnSetPositionOnUnseekableStream() { using(MemoryStream memoryStream = new MemoryStream()) { TestStream testStream = new TestStream(memoryStream, true, true, false); PartialStream partialStream = new PartialStream(testStream, 0, 0); Assert.Throws( delegate() { partialStream.Position = 0; } ); } } /// /// Tests whether the Read() method throws an exception if the stream does /// not support reading /// [Test] public void TestThrowOnReadFromUnreadableStream() { using(MemoryStream memoryStream = new MemoryStream()) { TestStream testStream = new TestStream(memoryStream, false, true, true); PartialStream partialStream = new PartialStream(testStream, 0, 0); byte[] test = new byte[10]; Assert.Throws( delegate() { Console.WriteLine(partialStream.Read(test, 0, 10)); } ); } } /// /// Tests whether the Seek() method of the partial stream is working /// [Test] public void TestSeeking() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(20); PartialStream partialStream = new PartialStream(memoryStream, 0, 20); Assert.AreEqual(7, partialStream.Seek(-13, SeekOrigin.End)); Assert.AreEqual(14, partialStream.Seek(7, SeekOrigin.Current)); Assert.AreEqual(11, partialStream.Seek(11, SeekOrigin.Begin)); } } /// /// Tests whether the Seek() method throws an exception if an invalid /// reference point is provided /// [Test] public void TestThrowOnInvalidSeekReferencePoint() { using(MemoryStream memoryStream = new MemoryStream()) { PartialStream partialStream = new PartialStream(memoryStream, 0, 0); Assert.Throws( delegate() { partialStream.Seek(1, (SeekOrigin)12345); } ); } } /// /// Verifies that the partial stream throws an exception if the attempt is /// made to change the length of the stream /// [Test] public void TestThrowOnLengthChange() { using(MemoryStream memoryStream = new MemoryStream()) { PartialStream partialStream = new PartialStream(memoryStream, 0, 0); Assert.Throws( delegate() { partialStream.SetLength(123); } ); } } /// /// Tests whether the Read() method returns 0 bytes if the attempt is made /// to read data from an invalid position /// [Test] public void TestReadFromInvalidPosition() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); memoryStream.Position = 1123; byte[] test = new byte[10]; Assert.AreEqual(0, memoryStream.Read(test, 0, 10)); } } /// Verifies that the Read() method is working [Test] public void TestReadFromPartialStream() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); memoryStream.Position = 100; memoryStream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 0, 10); PartialStream partialStream = new PartialStream(memoryStream, 95, 10); byte[] buffer = new byte[15]; int bytesRead = partialStream.Read(buffer, 0, 15); Assert.AreEqual(10, bytesRead); Assert.AreEqual( new byte[] { 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0 }, buffer ); } } /// Verifies that the Write() method is working [Test] public void TestWriteToPartialStream() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(123); memoryStream.Position = 60; memoryStream.Write(new byte[] { 11, 12, 13, 14, 15 }, 0, 5); PartialStream partialStream = new PartialStream(memoryStream, 50, 15); partialStream.Position = 3; partialStream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 0, 10); byte[] buffer = new byte[17]; memoryStream.Position = 49; int bytesRead = memoryStream.Read(buffer, 0, 17); Assert.AreEqual(17, bytesRead); Assert.AreEqual( new byte[] { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14, 15, 0 }, buffer ); } } /// /// Verifies that an exception is thrown if the Write() method of the partial stream /// is attempted to be used to extend the partial stream's length /// [Test] public void TestThrowOnExtendPartialStream() { using(MemoryStream memoryStream = new MemoryStream()) { memoryStream.SetLength(25); PartialStream partialStream = new PartialStream(memoryStream, 10, 10); partialStream.Position = 5; Assert.Throws( delegate() { partialStream.Write(new byte[] { 1, 2, 3, 4, 5, 6 }, 0, 6); } ); } } } } // namespace Nuclex.Support.IO #endif // UNITTEST