using System; using System.IO; using UnitTestSharp; namespace Amphora.UnitTests { public class ChunkedBytesTests : TestFixture { public static byte[] Count(int length) { byte[] returnMe = new byte[length]; for (int i = 0; i < length; ++i) { returnMe[i] = (byte)i; } return returnMe; } public class ShortBytes : IChunkedBytes { private readonly byte[] bytes; private readonly int size; public ShortBytes(byte[] bytes, int size) { this.bytes = bytes; this.size = size; } public long Length => bytes.Length; public ByteView GetChunk(long start) { return new ByteView(bytes, (int)start, (int)Math.Min(start + size, bytes.Length)); } } public class EmptyChunks : IChunkedBytes { public long Length => 4; public ByteView GetChunk(long start) { return new ByteView(new byte[4], 0, 0); } } public class UnseekableStream : MemoryStream { public UnseekableStream(byte[] bytes) : base(bytes) { } public override bool CanSeek => false; } public class OverstatedLengthStream : MemoryStream { public OverstatedLengthStream(byte[] bytes) : base(bytes) { } public override long Length => base.Length + 10; } public class ByteChunksTests : TestFixture { public void CtorThrowsOnADefaultView() { CheckThrow(typeof(ArgumentException)); new ByteChunks(default(ByteView)); } public void LengthIsTheViewsLength() { CheckEqual(3L, new ByteChunks(new ByteView(Count(8), 2, 5)).Length); } public void LengthOfAnEmptyViewIsZero() { CheckEqual(0L, new ByteChunks(new ByteView(new byte[0])).Length); } public void TheChunkAtZeroIsTheWholeView() { ByteView chunk = new ByteChunks(new ByteView(Count(4))).GetChunk(0); CheckEqual(4, chunk.Length); CheckEqual((byte)0, chunk[0]); CheckEqual((byte)3, chunk[3]); } public void TheChunkAtAnIndexRunsToTheEndOfTheView() { ByteView chunk = new ByteChunks(new ByteView(Count(8), 2, 6)).GetChunk(1); CheckEqual(3, chunk.Length); CheckEqual((byte)3, chunk[0]); CheckEqual((byte)5, chunk[2]); } public void TheChunkAtTheLastIndexIsTheLastByte() { ByteView chunk = new ByteChunks(new ByteView(Count(4))).GetChunk(3); CheckEqual(1, chunk.Length); CheckEqual((byte)3, chunk[0]); } public void TheChunkViewsTheArrayItself() { byte[] bytes = Count(4); CheckTrue(new ByteChunks(new ByteView(bytes)).GetChunk(1).IsOver(bytes)); } public void AStartAtTheLengthThrows() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).GetChunk(4); } public void ANegativeStartThrows() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).GetChunk(-1); } public void AnyStartOnAnEmptyViewThrows() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(new byte[0])).GetChunk(0); } } public class StreamChunksTests : TestFixture { public void CtorThrowsOnNull() { CheckThrow(typeof(ArgumentNullException)); new StreamChunks(null); } public void CtorThrowsOnAStreamThatCannotSeek() { CheckThrow(typeof(ArgumentException)); new StreamChunks(new UnseekableStream(Count(4))); } public void CtorThrowsOnAChunkSizeOfZero() { CheckThrow(typeof(ArgumentOutOfRangeException)); new StreamChunks(new MemoryStream(Count(4)), chunkSize: 0); } public void LengthIsTheStreamsLength() { CheckEqual(9L, new StreamChunks(new MemoryStream(Count(9))).Length); } public void TheChunkAtZeroHoldsTheFirstChunkSizeBytes() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10)), chunkSize: 4); ByteView chunk = chunks.GetChunk(0); CheckEqual(4, chunk.Length); CheckEqual((byte)0, chunk[0]); CheckEqual((byte)3, chunk[3]); } public void TheChunkAtAnIndexStartsThere() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10)), chunkSize: 4); ByteView chunk = chunks.GetChunk(5); CheckEqual(4, chunk.Length); CheckEqual((byte)5, chunk[0]); } public void TheLastChunkHoldsOnlyWhatIsLeft() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10)), chunkSize: 4); ByteView chunk = chunks.GetChunk(8); CheckEqual(2, chunk.Length); CheckEqual((byte)9, chunk[1]); } public void ChunksCanBeReadOutOfOrder() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10)), chunkSize: 4); chunks.GetChunk(6); CheckEqual((byte)1, chunks.GetChunk(1)[0]); } public void ReadingTheSameStartTwice_GivesTheSameBytes() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10)), chunkSize: 4); chunks.GetChunk(2); CheckEqual((byte)2, chunks.GetChunk(2)[0]); } public void AChunkLastsOnlyUntilTheNextCall() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10)), chunkSize: 4); ByteView first = chunks.GetChunk(0); chunks.GetChunk(4); CheckEqual((byte)4, first[0]); } public void AStartAtTheLengthThrows() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10))); CheckThrow(typeof(ArgumentOutOfRangeException)); chunks.GetChunk(10); } public void ANegativeStartThrows() { StreamChunks chunks = new StreamChunks(new MemoryStream(Count(10))); CheckThrow(typeof(ArgumentOutOfRangeException)); chunks.GetChunk(-1); } public void AStreamThatEndsBeforeItsLength_Throws() { StreamChunks chunks = new StreamChunks(new OverstatedLengthStream(Count(10))); CheckThrow(typeof(EndOfStreamException)); chunks.GetChunk(12); } } public class StreamChunksDisposeTests : TestFixture { public void DisposesTheStream() { MemoryStream stream = new MemoryStream(Count(4)); new StreamChunks(stream).Dispose(); CheckFalse(stream.CanRead); } public void LeavesTheStreamOpenWhenToldTo() { MemoryStream stream = new MemoryStream(Count(4)); new StreamChunks(stream, leaveOpen: true).Dispose(); CheckTrue(stream.CanRead); } } public class CopyToViewTests : TestFixture { public void FillsTheViewAcrossChunks() { byte[] destination = new byte[9]; int copied = new ShortBytes(Count(8), size: 3).CopyTo(2, new ByteView(destination, 1, 7)); CheckEqual(6, copied); CheckEqual(new byte[] { 0, 2, 3, 4, 5, 6, 7, 0, 0 }, destination); } public void ABytesRunThatEndsFirst_FillsWhatThereIsAndSaysHowMany() { byte[] destination = new byte[8]; int copied = new ShortBytes(Count(4), size: 3).CopyTo(1, new ByteView(destination)); CheckEqual(3, copied); CheckEqual(new byte[] { 1, 2, 3, 0, 0, 0, 0, 0 }, destination); } public void AnEmptyView_CopiesNothing() { CheckEqual(0, new ByteChunks(new ByteView(Count(4))).CopyTo(0, new ByteView(new byte[0]))); } public void NullChunks_Throws() { CheckThrow(typeof(ArgumentNullException)); ChunkedBytesExtensions.CopyTo(null, 0, new ByteView(new byte[2])); } public void AStartPastTheLength_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).CopyTo(5, new ByteView(new byte[2])); } } public class CopyToTests : TestFixture { public void WithinOneChunk_CopiesTheBytes() { byte[] destination = new byte[4]; int copied = new ByteChunks(new ByteView(Count(8))).CopyTo(2, destination, 0, 4); CheckEqual(4, copied); CheckEqual((byte)2, destination[0]); CheckEqual((byte)5, destination[3]); } public void AcrossChunks_CopiesTheBytesInOrder() { byte[] destination = new byte[8]; int copied = new ShortBytes(Count(8), size: 3).CopyTo(0, destination, 0, 8); CheckEqual(8, copied); for (int i = 0; i < 8; ++i) { CheckEqual((byte)i, destination[i]); } } public void StartingMidChunk_CopiesFromThere() { byte[] destination = new byte[5]; new ShortBytes(Count(8), size: 3).CopyTo(4, destination, 0, 4); CheckEqual((byte)4, destination[0]); CheckEqual((byte)7, destination[3]); } public void IntoTheMiddleOfTheDestination_LeavesTheRestAlone() { byte[] destination = new byte[5]; new ShortBytes(Count(8), size: 3).CopyTo(0, destination, 2, 2); CheckEqual((byte)0, destination[0]); CheckEqual((byte)0, destination[1]); CheckEqual((byte)0, destination[2]); CheckEqual((byte)1, destination[3]); CheckEqual((byte)0, destination[4]); } public void PastTheEnd_CopiesWhatIsLeftAndSaysHowMany() { byte[] destination = new byte[8]; int copied = new ShortBytes(Count(8), size: 3).CopyTo(5, destination, 0, 8); CheckEqual(3, copied); CheckEqual((byte)7, destination[2]); } public void StartingAtTheLength_CopiesNothing() { CheckEqual(0, new ByteChunks(new ByteView(Count(4))).CopyTo(4, new byte[2], 0, 2)); } public void ACountOfZero_CopiesNothing() { CheckEqual(0, new ByteChunks(new ByteView(Count(4))).CopyTo(0, new byte[2], 0, 0)); } public void NullChunks_Throws() { CheckThrow(typeof(ArgumentNullException)); ChunkedBytesExtensions.CopyTo(null, 0, new byte[2], 0, 2); } public void ANullDestination_Throws() { CheckThrow(typeof(ArgumentNullException)); new ByteChunks(new ByteView(Count(4))).CopyTo(0, null, 0, 2); } public void ACountThatDoesNotFitTheDestination_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).CopyTo(0, new byte[2], 1, 2); } public void ANegativeCount_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).CopyTo(0, new byte[2], 0, -1); } public void ANegativeDestinationIndex_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).CopyTo(0, new byte[2], -1, 1); } public void ANegativeStart_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).CopyTo(-1, new byte[2], 0, 1); } public void AStartPastTheLength_Throws() { CheckThrow(typeof(ArgumentOutOfRangeException)); new ByteChunks(new ByteView(Count(4))).CopyTo(5, new byte[2], 0, 1); } public void AnEmptyChunk_ThrowsInsteadOfLoopingForever() { CheckThrow(typeof(InvalidOperationException)); new EmptyChunks().CopyTo(0, new byte[2], 0, 2); } } } }