Moved all unit test files into a separate directory in preparation for splitting the project
This commit is contained in:
parent
28b96fd557
commit
ba5234f701
58 changed files with 0 additions and 853 deletions
561
Tests/IO/ChainStreamTest.cs
Normal file
561
Tests/IO/ChainStreamTest.cs
Normal file
|
@ -0,0 +1,561 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.IO {
|
||||
|
||||
/// <summary>Unit Test for the stream chainer</summary>
|
||||
[TestFixture]
|
||||
internal class ChainStreamTest {
|
||||
|
||||
#region class TestStream
|
||||
|
||||
/// <summary>Testing stream that allows specific features to be disabled</summary>
|
||||
private class TestStream : Stream {
|
||||
|
||||
/// <summary>Initializes a new test stream</summary>
|
||||
/// <param name="wrappedStream">Stream that will be wrapped</param>
|
||||
/// <param name="allowRead">Whether to allow reading from the stream</param>
|
||||
/// <param name="allowWrite">Whether to allow writing to the stream</param>
|
||||
/// <param name="allowSeek">Whether to allow seeking within the stream</param>
|
||||
public TestStream(
|
||||
Stream wrappedStream, bool allowRead, bool allowWrite, bool allowSeek
|
||||
) {
|
||||
this.stream = wrappedStream;
|
||||
this.readAllowed = allowRead;
|
||||
this.writeAllowed = allowWrite;
|
||||
this.seekAllowed = allowSeek;
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be read from the stream</summary>
|
||||
public override bool CanRead {
|
||||
get { return this.readAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether the stream supports seeking</summary>
|
||||
public override bool CanSeek {
|
||||
get { return this.seekAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be written into the stream</summary>
|
||||
public override bool CanWrite {
|
||||
get { return this.writeAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all buffers for this stream and causes any buffered data to be written
|
||||
/// to the underlying device.
|
||||
/// </summary>
|
||||
public override void Flush() {
|
||||
++this.flushCallCount;
|
||||
this.stream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>Length of the stream in bytes</summary>
|
||||
public override long Length {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Absolute position of the file pointer within the stream</summary>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// At least one of the chained streams does not support seeking
|
||||
/// </exception>
|
||||
public override long Position {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Position;
|
||||
}
|
||||
set {
|
||||
enforceSeekAllowed();
|
||||
this.stream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the stream and advances the position of
|
||||
/// the file pointer by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer that will receive the data read from the stream</param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the stream will place the data read
|
||||
/// </param>
|
||||
/// <param name="count">Maximum number of bytes that will be read</param>
|
||||
/// <returns>
|
||||
/// The number of bytes that were actually read from the stream and written into
|
||||
/// the provided buffer
|
||||
/// </returns>
|
||||
public override int Read(byte[] buffer, int offset, int count) {
|
||||
enforceReadAllowed();
|
||||
return this.stream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Changes the position of the file pointer</summary>
|
||||
/// <param name="offset">
|
||||
/// Offset to move the file pointer by, relative to the position indicated by
|
||||
/// the <paramref name="origin" /> parameter.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// Reference point relative to which the file pointer is placed
|
||||
/// </param>
|
||||
/// <returns>The new absolute position within the stream</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin) {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>Changes the length of the stream</summary>
|
||||
/// <param name="value">New length the stream shall have</param>
|
||||
public override void SetLength(long value) {
|
||||
enforceSeekAllowed();
|
||||
this.stream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the stream and advances the position of
|
||||
/// the file pointer by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// Buffer containing the data that will be written to the stream
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the data to be written starts
|
||||
/// </param>
|
||||
/// <param name="count">Number of bytes that will be written into the stream</param>
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
enforceWriteAllowed();
|
||||
this.stream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
public int FlushCallCount {
|
||||
get { return this.flushCallCount; }
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if reading is not allowed</summary>
|
||||
private void enforceReadAllowed() {
|
||||
if(!this.readAllowed) {
|
||||
throw new NotSupportedException("Reading has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if writing is not allowed</summary>
|
||||
private void enforceWriteAllowed() {
|
||||
if(!this.writeAllowed) {
|
||||
throw new NotSupportedException("Writing has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if seeking is not allowed</summary>
|
||||
private void enforceSeekAllowed() {
|
||||
if(!this.seekAllowed) {
|
||||
throw new NotSupportedException("Seeking has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stream being wrapped for testing</summary>
|
||||
private Stream stream;
|
||||
/// <summary>whether to allow reading from the wrapped stream</summary>
|
||||
private bool readAllowed;
|
||||
/// <summary>Whether to allow writing to the wrapped stream</summary>
|
||||
private bool writeAllowed;
|
||||
/// <summary>Whether to allow seeking within the wrapped stream</summary>
|
||||
private bool seekAllowed;
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
private int flushCallCount;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestStream
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream chainer correctly partitions a long write request
|
||||
/// over its chained streams and appends any remaining data to the end of
|
||||
/// the last chained stream.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPartitionedWrite() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
byte[] testData = new byte[20];
|
||||
for(int index = 0; index < testData.Length; ++index) {
|
||||
testData[index] = (byte)(index + 1);
|
||||
}
|
||||
|
||||
chainer.Position = 5;
|
||||
chainer.Write(testData, 0, testData.Length);
|
||||
|
||||
Assert.AreEqual(
|
||||
new byte[] { 0, 0, 0, 0, 0, 1, 2, 3, 4, 5 },
|
||||
((MemoryStream)chainer.ChainedStreams[0]).ToArray()
|
||||
);
|
||||
Assert.AreEqual(
|
||||
new byte[] { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 },
|
||||
((MemoryStream)chainer.ChainedStreams[1]).ToArray()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream chainer correctly partitions a long read request
|
||||
/// over its chained streams.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPartitionedRead() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
((MemoryStream)chainer.ChainedStreams[0]).Write(
|
||||
new byte[] { 1, 2, 3, 4, 5 }, 0, 5
|
||||
);
|
||||
((MemoryStream)chainer.ChainedStreams[1]).Write(
|
||||
new byte[] { 6, 7, 8, 9, 10 }, 0, 5
|
||||
);
|
||||
|
||||
chainer.Position = 3;
|
||||
byte[] buffer = new byte[15];
|
||||
int bytesRead = chainer.Read(buffer, 0, 14);
|
||||
|
||||
Assert.AreEqual(14, bytesRead);
|
||||
Assert.AreEqual(new byte[] { 4, 5, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 0, 0, 0 }, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream chainer can handle a stream resize
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteAfterResize() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
// The first stream has a size of 10 bytes, so this goes into the second stream
|
||||
chainer.Position = 11;
|
||||
chainer.Write(new byte[] { 12, 34 }, 0, 2);
|
||||
|
||||
// Now we resize the first stream to 15 bytes, so this goes into the first stream
|
||||
((MemoryStream)chainer.ChainedStreams[0]).SetLength(15);
|
||||
chainer.Write(new byte[] { 56, 78, 11, 22 }, 0, 4);
|
||||
|
||||
Assert.AreEqual(
|
||||
new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 78 },
|
||||
((MemoryStream)chainer.ChainedStreams[0]).ToArray()
|
||||
);
|
||||
Assert.AreEqual(
|
||||
new byte[] { 11, 22, 34, 0, 0, 0, 0, 0, 0, 0 },
|
||||
((MemoryStream)chainer.ChainedStreams[1]).ToArray()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests writing to a stream chainer that contains an unseekable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteToUnseekableStream() {
|
||||
MemoryStream firstStream = new MemoryStream();
|
||||
|
||||
// Now the second stream _does_ support seeking. If the stream chainer ignores
|
||||
// that, it would overwrite data in the second stream.
|
||||
MemoryStream secondStream = new MemoryStream();
|
||||
secondStream.Write(new byte[] { 0, 9, 8, 7, 6 }, 0, 5);
|
||||
secondStream.Position = 0;
|
||||
|
||||
TestStream testStream = new TestStream(firstStream, true, true, false);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream, secondStream });
|
||||
|
||||
chainer.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);
|
||||
Assert.IsFalse(chainer.CanSeek);
|
||||
Assert.AreEqual(0, firstStream.Length);
|
||||
Assert.AreEqual(new byte[] { 0, 9, 8, 7, 6, 1, 2, 3, 4, 5 }, secondStream.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests reading from a stream chainer that contains an unseekable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReadFromUnseekableStream() {
|
||||
MemoryStream firstStream = new MemoryStream();
|
||||
|
||||
// Now the second stream _does_ support seeking. If the stream chainer ignores
|
||||
// that, it would overwrite data in the second stream.
|
||||
MemoryStream secondStream = new MemoryStream();
|
||||
secondStream.Write(new byte[] { 0, 9, 8, 7, 6 }, 0, 5);
|
||||
secondStream.Position = 3;
|
||||
|
||||
TestStream testStream = new TestStream(firstStream, true, true, false);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream, secondStream });
|
||||
|
||||
Assert.IsFalse(chainer.CanSeek);
|
||||
|
||||
byte[] buffer = new byte[5];
|
||||
int readByteCount = chainer.Read(buffer, 0, 3);
|
||||
|
||||
Assert.AreEqual(3, readByteCount);
|
||||
Assert.AreEqual(new byte[] { 0, 9, 8, 0, 0 }, buffer);
|
||||
|
||||
readByteCount = chainer.Read(buffer, 0, 3);
|
||||
|
||||
Assert.AreEqual(2, readByteCount);
|
||||
Assert.AreEqual(new byte[] { 7, 6, 8, 0, 0 }, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests reading from a stream chainer that contains an unreadable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReadFromUnreadableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, false, true, true);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Read(new byte[5], 0, 5); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests writing to a stream chainer that contains an unwriteable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnWriteToUnwriteableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, false, true);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the stream chainer throws an exception if the attempt is
|
||||
/// made to change the length of the stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnLengthChange() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.SetLength(123); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CanRead property is correctly determined by the stream chainer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanRead() {
|
||||
MemoryStream yesStream = new MemoryStream();
|
||||
TestStream noStream = new TestStream(yesStream, false, true, true);
|
||||
|
||||
Stream[] yesGroup = new Stream[] { yesStream, yesStream, yesStream, yesStream };
|
||||
Stream[] partialGroup = new Stream[] { yesStream, yesStream, noStream, yesStream };
|
||||
Stream[] noGroup = new Stream[] { noStream, noStream, noStream, noStream };
|
||||
|
||||
Assert.IsTrue(new ChainStream(yesGroup).CanRead);
|
||||
Assert.IsFalse(new ChainStream(partialGroup).CanRead);
|
||||
Assert.IsFalse(new ChainStream(noGroup).CanRead);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CanRead property is correctly determined by the stream chainer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanWrite() {
|
||||
MemoryStream yesStream = new MemoryStream();
|
||||
TestStream noStream = new TestStream(yesStream, true, false, true);
|
||||
|
||||
Stream[] yesGroup = new Stream[] { yesStream, yesStream, yesStream, yesStream };
|
||||
Stream[] partialGroup = new Stream[] { yesStream, yesStream, noStream, yesStream };
|
||||
Stream[] noGroup = new Stream[] { noStream, noStream, noStream, noStream };
|
||||
|
||||
Assert.IsTrue(new ChainStream(yesGroup).CanWrite);
|
||||
Assert.IsFalse(new ChainStream(partialGroup).CanWrite);
|
||||
Assert.IsFalse(new ChainStream(noGroup).CanWrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CanSeek property is correctly determined by the stream chainer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanSeek() {
|
||||
MemoryStream yesStream = new MemoryStream();
|
||||
TestStream noStream = new TestStream(yesStream, true, true, false);
|
||||
|
||||
Stream[] yesGroup = new Stream[] { yesStream, yesStream, yesStream, yesStream };
|
||||
Stream[] partialGroup = new Stream[] { yesStream, yesStream, noStream, yesStream };
|
||||
Stream[] noGroup = new Stream[] { noStream, noStream, noStream, noStream };
|
||||
|
||||
Assert.IsTrue(new ChainStream(yesGroup).CanSeek);
|
||||
Assert.IsFalse(new ChainStream(partialGroup).CanSeek);
|
||||
Assert.IsFalse(new ChainStream(noGroup).CanSeek);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Seek() method is called on
|
||||
/// a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSeekWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Seek(123, SeekOrigin.Begin); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Position property is retrieved
|
||||
/// on a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnGetPositionWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(chainer.Position); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Position property is set
|
||||
/// on a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSetPositionWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Position = 123; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Length property is retrieved
|
||||
/// on a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnGetLengthWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Assert.IsTrue(chainer.Length != chainer.Length); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method of the stream chainer is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSeeking() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
Assert.AreEqual(7, chainer.Seek(-13, SeekOrigin.End));
|
||||
Assert.AreEqual(14, chainer.Seek(7, SeekOrigin.Current));
|
||||
Assert.AreEqual(11, chainer.Seek(11, SeekOrigin.Begin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream behaves correctly if data is read from beyond its end
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReadBeyondEndOfStream() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
chainer.Seek(10, SeekOrigin.End);
|
||||
|
||||
// This is how the MemoryStream behaves: it returns 0 bytes.
|
||||
int readByteCount = chainer.Read(new byte[1], 0, 1);
|
||||
Assert.AreEqual(0, readByteCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method throws an exception if an invalid
|
||||
/// reference point is provided
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidSeekReferencePoint() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { chainer.Seek(1, (SeekOrigin)12345); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the position property works correctly</summary>
|
||||
[Test]
|
||||
public void TestPositionChange() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
chainer.Position = 7;
|
||||
Assert.AreEqual(chainer.Position, 7);
|
||||
chainer.Position = 14;
|
||||
Assert.AreEqual(chainer.Position, 14);
|
||||
}
|
||||
|
||||
/// <summary>Tests the Flush() method of the stream chainer</summary>
|
||||
[Test]
|
||||
public void TestFlush() {
|
||||
MemoryStream firstStream = new MemoryStream();
|
||||
TestStream firstTestStream = new TestStream(firstStream, true, true, true);
|
||||
|
||||
MemoryStream secondStream = new MemoryStream();
|
||||
TestStream secondTestStream = new TestStream(secondStream, true, true, true);
|
||||
|
||||
ChainStream chainer = new ChainStream(
|
||||
new Stream[] { firstTestStream, secondTestStream }
|
||||
);
|
||||
|
||||
Assert.AreEqual(0, firstTestStream.FlushCallCount);
|
||||
Assert.AreEqual(0, secondTestStream.FlushCallCount);
|
||||
|
||||
chainer.Flush();
|
||||
|
||||
Assert.AreEqual(1, firstTestStream.FlushCallCount);
|
||||
Assert.AreEqual(1, secondTestStream.FlushCallCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a stream chainer with two streams that each have a size of 10 bytes
|
||||
/// </summary>
|
||||
/// <returns>The new stream chainer with two chained 10-byte streams</returns>
|
||||
private static ChainStream chainTwoStreamsOfTenBytes() {
|
||||
MemoryStream firstStream = new MemoryStream(10);
|
||||
MemoryStream secondStream = new MemoryStream(10);
|
||||
|
||||
firstStream.SetLength(10);
|
||||
secondStream.SetLength(10);
|
||||
|
||||
return new ChainStream(
|
||||
new Stream[] { firstStream, secondStream }
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.IO
|
||||
|
||||
#endif // UNITTEST
|
526
Tests/IO/PartialStreamTest.cs
Normal file
526
Tests/IO/PartialStreamTest.cs
Normal file
|
@ -0,0 +1,526 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.IO {
|
||||
|
||||
/// <summary>Unit Test for the partial stream</summary>
|
||||
[TestFixture]
|
||||
internal class PartialStreamTest {
|
||||
|
||||
#region class TestStream
|
||||
|
||||
/// <summary>Testing stream that allows specific features to be disabled</summary>
|
||||
private class TestStream : Stream {
|
||||
|
||||
/// <summary>Initializes a new test stream</summary>
|
||||
/// <param name="wrappedStream">Stream that will be wrapped</param>
|
||||
/// <param name="allowRead">Whether to allow reading from the stream</param>
|
||||
/// <param name="allowWrite">Whether to allow writing to the stream</param>
|
||||
/// <param name="allowSeek">Whether to allow seeking within the stream</param>
|
||||
public TestStream(
|
||||
Stream wrappedStream, bool allowRead, bool allowWrite, bool allowSeek
|
||||
) {
|
||||
this.stream = wrappedStream;
|
||||
this.readAllowed = allowRead;
|
||||
this.writeAllowed = allowWrite;
|
||||
this.seekAllowed = allowSeek;
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be read from the stream</summary>
|
||||
public override bool CanRead {
|
||||
get { return this.readAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether the stream supports seeking</summary>
|
||||
public override bool CanSeek {
|
||||
get { return this.seekAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be written into the stream</summary>
|
||||
public override bool CanWrite {
|
||||
get { return this.writeAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all buffers for this stream and causes any buffered data to be written
|
||||
/// to the underlying device.
|
||||
/// </summary>
|
||||
public override void Flush() {
|
||||
++this.flushCallCount;
|
||||
this.stream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>Length of the stream in bytes</summary>
|
||||
public override long Length {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Absolute position of the file pointer within the stream</summary>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// At least one of the chained streams does not support seeking
|
||||
/// </exception>
|
||||
public override long Position {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Position;
|
||||
}
|
||||
set {
|
||||
enforceSeekAllowed();
|
||||
this.stream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the stream and advances the position of
|
||||
/// the file pointer by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer that will receive the data read from the stream</param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the stream will place the data read
|
||||
/// </param>
|
||||
/// <param name="count">Maximum number of bytes that will be read</param>
|
||||
/// <returns>
|
||||
/// The number of bytes that were actually read from the stream and written into
|
||||
/// the provided buffer
|
||||
/// </returns>
|
||||
public override int Read(byte[] buffer, int offset, int count) {
|
||||
enforceReadAllowed();
|
||||
return this.stream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Changes the position of the file pointer</summary>
|
||||
/// <param name="offset">
|
||||
/// Offset to move the file pointer by, relative to the position indicated by
|
||||
/// the <paramref name="origin" /> parameter.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// Reference point relative to which the file pointer is placed
|
||||
/// </param>
|
||||
/// <returns>The new absolute position within the stream</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin) {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>Changes the length of the stream</summary>
|
||||
/// <param name="value">New length the stream shall have</param>
|
||||
public override void SetLength(long value) {
|
||||
enforceSeekAllowed();
|
||||
this.stream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the stream and advances the position of
|
||||
/// the file pointer by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// Buffer containing the data that will be written to the stream
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the data to be written starts
|
||||
/// </param>
|
||||
/// <param name="count">Number of bytes that will be written into the stream</param>
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
enforceWriteAllowed();
|
||||
this.stream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
public int FlushCallCount {
|
||||
get { return this.flushCallCount; }
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if reading is not allowed</summary>
|
||||
private void enforceReadAllowed() {
|
||||
if(!this.readAllowed) {
|
||||
throw new NotSupportedException("Reading has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if writing is not allowed</summary>
|
||||
private void enforceWriteAllowed() {
|
||||
if(!this.writeAllowed) {
|
||||
throw new NotSupportedException("Writing has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if seeking is not allowed</summary>
|
||||
private void enforceSeekAllowed() {
|
||||
if(!this.seekAllowed) {
|
||||
throw new NotSupportedException("Seeking has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stream being wrapped for testing</summary>
|
||||
private Stream stream;
|
||||
/// <summary>whether to allow reading from the wrapped stream</summary>
|
||||
private bool readAllowed;
|
||||
/// <summary>Whether to allow writing to the wrapped stream</summary>
|
||||
private bool writeAllowed;
|
||||
/// <summary>Whether to allow seeking within the wrapped stream</summary>
|
||||
private bool seekAllowed;
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
private int flushCallCount;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestStream
|
||||
|
||||
/// <summary>Tests whether the partial stream constructor is working</summary>
|
||||
[Test]
|
||||
public void TestConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 23, 100);
|
||||
Assert.AreEqual(100, partialStream.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream constructor throws an exception if
|
||||
/// it's invoked with an invalid start offset
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidStartInConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { Console.WriteLine(new PartialStream(memoryStream, -1, 10)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream constructor throws an exception if
|
||||
/// it's invoked with an invalid start offset
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidLengthInConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { Console.WriteLine(new PartialStream(memoryStream, 100, 24)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream constructor throws an exception if
|
||||
/// it's invoked with a start offset on an unseekable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnUnseekableStreamWithOffsetInConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { Console.WriteLine(new PartialStream(testStream, 23, 100)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CanRead property reports its status correctly
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CanWrite property reports its status correctly
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CanSeek property reports its status correctly
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CompleteStream property returns the original stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCompleteStreamProperty() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
Assert.AreSame(memoryStream, partialStream.CompleteStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Flush() method can be called</summary>
|
||||
[Test]
|
||||
public void TestFlush() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
partialStream.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property correctly reports the file pointer position
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property is correctly updated
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property throws an exception if the stream does
|
||||
/// not support seeking.
|
||||
/// </summary>
|
||||
[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<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(partialStream.Position); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property throws an exception if the stream does
|
||||
/// not support seeking.
|
||||
/// </summary>
|
||||
[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<NotSupportedException>(
|
||||
delegate() { partialStream.Position = 0; }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Read() method throws an exception if the stream does
|
||||
/// not support reading
|
||||
/// </summary>
|
||||
[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<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(partialStream.Read(test, 0, 10)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method of the partial stream is working
|
||||
/// </summary>
|
||||
[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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method throws an exception if an invalid
|
||||
/// reference point is provided
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidSeekReferencePoint() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { partialStream.Seek(1, (SeekOrigin)12345); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream throws an exception if the attempt is
|
||||
/// made to change the length of the stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnLengthChange() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { partialStream.SetLength(123); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Read() method returns 0 bytes if the attempt is made
|
||||
/// to read data from an invalid position
|
||||
/// </summary>
|
||||
[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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the Read() method is working</summary>
|
||||
[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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the Write() method is working</summary>
|
||||
[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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnExtendPartialStream() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(25);
|
||||
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 10, 10);
|
||||
partialStream.Position = 5;
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { partialStream.Write(new byte[] { 1, 2, 3, 4, 5, 6 }, 0, 6); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.IO
|
||||
|
||||
#endif // UNITTEST
|
329
Tests/IO/RingMemoryStreamTest.cs
Normal file
329
Tests/IO/RingMemoryStreamTest.cs
Normal file
|
@ -0,0 +1,329 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.IO {
|
||||
|
||||
/// <summary>Unit Test for the ring buffer class</summary>
|
||||
[TestFixture]
|
||||
internal class RingMemoryStreamTest {
|
||||
|
||||
/// <summary>Prepares some test data for the units test methods</summary>
|
||||
[TestFixtureSetUp]
|
||||
public void Setup() {
|
||||
this.testBytes = new byte[20];
|
||||
for(int i = 0; i < 20; ++i)
|
||||
this.testBytes[i] = (byte)i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer blocks write attempts that would exceed its capacity
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteTooLargeChunk() {
|
||||
Assert.Throws<OverflowException>(
|
||||
delegate() { new RingMemoryStream(10).Write(this.testBytes, 0, 11); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer still accepts write attempts that would fill the
|
||||
/// entire buffer in one go.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteBarelyFittingChunk() {
|
||||
new RingMemoryStream(10).Write(this.testBytes, 0, 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer correctly manages write attempts that have to
|
||||
/// be split at the end of the ring buffer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteSplitBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 7);
|
||||
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 0, 1, 2, 3, 4, 5, 6 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer correctly manages write attempts that write into
|
||||
/// the gap after the ring buffer's data has become split
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteSplitAndLinearBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 2);
|
||||
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 0, 1, 2, 3, 4, 0, 1 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer still detects write that would exceed its capacity
|
||||
/// if they write into the gap after the ring buffer's data has become split
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteSplitAndLinearTooLargeBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
Assert.Throws<OverflowException>(
|
||||
delegate() { testRing.Write(this.testBytes, 0, 3); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ring buffer correctly handles fragmentation</summary>
|
||||
[Test]
|
||||
public void TestSplitBlockWrappedRead() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 8, 9, 0, 1, 2, 3, 4 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ring buffer correctly handles fragmentation</summary>
|
||||
[Test]
|
||||
public void TestSplitBlockLinearRead() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
|
||||
byte[] actual = new byte[5];
|
||||
testRing.Read(actual, 0, 5);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 8, 9 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the ring buffer correctly returns partial data if more
|
||||
/// data is requested than is contained in it.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEndOfStream() {
|
||||
byte[] tempBytes = new byte[10];
|
||||
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
Assert.AreEqual(0, testRing.Read(tempBytes, 0, 5));
|
||||
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
Assert.AreEqual(5, testRing.Read(tempBytes, 0, 10));
|
||||
|
||||
testRing.Write(this.testBytes, 0, 6);
|
||||
testRing.Read(tempBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 9);
|
||||
Assert.AreEqual(10, testRing.Read(tempBytes, 0, 20));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the ring buffer can extend its capacity without loosing data
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCapacityIncrease() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
|
||||
testRing.Capacity = 20;
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
|
||||
Assert.AreEqual(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the ring buffer can reduce its capacity without loosing data
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCapacityDecrease() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(20);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
|
||||
testRing.Capacity = 10;
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
|
||||
Assert.AreEqual(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that an exception is thrown when the ring buffer's capacity is
|
||||
/// reduced so much it would have to give up some of its contained data
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCapacityDecreaseException() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(20);
|
||||
testRing.Write(this.testBytes, 0, 20);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(
|
||||
delegate() { testRing.Capacity = 10; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Capacity property returns the current capacity</summary>
|
||||
[Test]
|
||||
public void TestCapacity() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(123);
|
||||
|
||||
Assert.AreEqual(123, testRing.Capacity);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the CanRead property returns true</summary>
|
||||
[Test]
|
||||
public void TestCanRead() {
|
||||
Assert.IsTrue(new RingMemoryStream(10).CanRead);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the CanSeek property returns false</summary>
|
||||
[Test]
|
||||
public void TestCanSeek() {
|
||||
Assert.IsFalse(new RingMemoryStream(10).CanSeek);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the CanWrite property returns true</summary>
|
||||
[Test]
|
||||
public void TestCanWrite() {
|
||||
Assert.IsTrue(new RingMemoryStream(10).CanWrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the auto reset feature works (resets the buffer pointer to the
|
||||
/// left end of the buffer when it gets empty; mainly a performance feature).
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAutoReset() {
|
||||
byte[] tempBytes = new byte[10];
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(tempBytes, 0, 2);
|
||||
testRing.Read(tempBytes, 0, 2);
|
||||
testRing.Read(tempBytes, 0, 1);
|
||||
testRing.Read(tempBytes, 0, 1);
|
||||
|
||||
Assert.AreEqual(2, testRing.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the Position property of the ring
|
||||
/// memory stream is used to retrieve the current file pointer position
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRetrievePosition() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(new RingMemoryStream(10).Position); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the Position property of the ring
|
||||
/// memory stream is used to modify the current file pointer position
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAssignPosition() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { new RingMemoryStream(10).Position = 0; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the Seek() method of the ring memory
|
||||
/// stream is attempted to be used
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSeek() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { new RingMemoryStream(10).Seek(0, SeekOrigin.Begin); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the SetLength() method of the ring
|
||||
/// memory stream is attempted to be used
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSetLength() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { new RingMemoryStream(10).SetLength(10); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the Flush() method of the ring memory stream, which is either a dummy
|
||||
/// implementation or has no side effects
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFlush() {
|
||||
new RingMemoryStream(10).Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the length property is updated in accordance to the data written
|
||||
/// into the ring memory stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLengthOnLinearBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(new byte[10], 0, 10);
|
||||
|
||||
Assert.AreEqual(10, testRing.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the length property is updated in accordance to the data written
|
||||
/// into the ring memory stream when the data is split within the stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLengthOnSplitBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
|
||||
testRing.Write(new byte[10], 0, 10);
|
||||
testRing.Read(new byte[5], 0, 5);
|
||||
testRing.Write(new byte[5], 0, 5);
|
||||
|
||||
Assert.AreEqual(10, testRing.Length);
|
||||
}
|
||||
|
||||
/// <summary>Test data for the ring buffer unit tests</summary>
|
||||
private byte[] testBytes;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.IO
|
||||
|
||||
#endif // UNITTEST
|
Loading…
Add table
Add a link
Reference in a new issue