Last active
July 26, 2020 01:13
-
-
Save antonfirsov/304c2a9d008446c78f786ddbd161767c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Six Labors. | |
// Licensed under the Apache License, Version 2.0. | |
using System; | |
using System.IO; | |
using SixLabors.ImageSharp.IO; | |
using Xunit; | |
namespace SixLabors.ImageSharp.Tests.IO | |
{ | |
public class BufferedReadStreamTests | |
{ | |
private readonly Configuration configuration; | |
public BufferedReadStreamTests() | |
{ | |
this.configuration = Configuration.CreateDefaultInstance(); | |
} | |
public static readonly TheoryData<int> BufferSizes = new TheoryData<int>() { 1, 2, 4, 8, 16, 97, 503, 719, 1024, 8096, 64768 }; | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamCanReadSingleByteFromOrigin(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
byte[] expected = stream.ToArray(); | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
Assert.Equal(expected[0], reader.ReadByte()); | |
// We've read a whole chunk but increment by 1 in our reader. | |
Assert.Equal(bufferSize, stream.Position); | |
Assert.Equal(1, reader.Position); | |
} | |
// Position of the stream should be reset on disposal. | |
Assert.Equal(1, stream.Position); | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamCanReadSingleByteFromOffset(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
byte[] expected = stream.ToArray(); | |
const int offset = 5; | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
reader.Position = offset; | |
Assert.Equal(expected[offset], reader.ReadByte()); | |
// We've read a whole chunk but increment by 1 in our reader. | |
Assert.Equal(bufferSize + offset, stream.Position); | |
Assert.Equal(offset + 1, reader.Position); | |
} | |
Assert.Equal(offset + 1, stream.Position); | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamCanReadSubsequentSingleByteCorrectly(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
byte[] expected = stream.ToArray(); | |
int i; | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
for (i = 0; i < expected.Length; i++) | |
{ | |
Assert.Equal(expected[i], reader.ReadByte()); | |
Assert.Equal(i + 1, reader.Position); | |
if (i < bufferSize) | |
{ | |
Assert.Equal(stream.Position, bufferSize); | |
} | |
else if (i >= bufferSize && i < bufferSize * 2) | |
{ | |
// We should have advanced to the second chunk now. | |
Assert.Equal(stream.Position, bufferSize * 2); | |
} | |
else | |
{ | |
// We should have advanced to the third chunk now. | |
Assert.Equal(stream.Position, bufferSize * 3); | |
} | |
} | |
} | |
Assert.Equal(i, stream.Position); | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamCanReadMultipleBytesFromOrigin(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
var buffer = new byte[2]; | |
byte[] expected = stream.ToArray(); | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
Assert.Equal(2, reader.Read(buffer, 0, 2)); | |
Assert.Equal(expected[0], buffer[0]); | |
Assert.Equal(expected[1], buffer[1]); | |
// We've read a whole chunk but increment by the buffer length in our reader. | |
Assert.Equal(stream.Position, bufferSize); | |
Assert.Equal(buffer.Length, reader.Position); | |
} | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamCanReadSubsequentMultipleByteCorrectly(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
var buffer = new byte[2]; | |
byte[] expected = stream.ToArray(); | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
for (int i = 0, o = 0; i < expected.Length / 2; i++, o += 2) | |
{ | |
Assert.Equal(2, reader.Read(buffer, 0, 2)); | |
Assert.Equal(expected[o], buffer[0]); | |
Assert.Equal(expected[o + 1], buffer[1]); | |
Assert.Equal(o + 2, reader.Position); | |
int offset = i * 2; | |
if (offset < bufferSize) | |
{ | |
Assert.Equal(stream.Position, bufferSize); | |
} | |
else if (offset >= bufferSize && offset < bufferSize * 2) | |
{ | |
// We should have advanced to the second chunk now. | |
Assert.Equal(stream.Position, bufferSize * 2); | |
} | |
else | |
{ | |
// We should have advanced to the third chunk now. | |
Assert.Equal(stream.Position, bufferSize * 3); | |
} | |
} | |
} | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamCanReadSubsequentMultipleByteSpanCorrectly(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
Span<byte> buffer = new byte[2]; | |
byte[] expected = stream.ToArray(); | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
for (int i = 0, o = 0; i < expected.Length / 2; i++, o += 2) | |
{ | |
Assert.Equal(2, reader.Read(buffer, 0, 2)); | |
Assert.Equal(expected[o], buffer[0]); | |
Assert.Equal(expected[o + 1], buffer[1]); | |
Assert.Equal(o + 2, reader.Position); | |
int offset = i * 2; | |
if (offset < bufferSize) | |
{ | |
Assert.Equal(stream.Position, bufferSize); | |
} | |
else if (offset >= bufferSize && offset < bufferSize * 2) | |
{ | |
// We should have advanced to the second chunk now. | |
Assert.Equal(stream.Position, bufferSize * 2); | |
} | |
else | |
{ | |
// We should have advanced to the third chunk now. | |
Assert.Equal(stream.Position, bufferSize * 3); | |
} | |
} | |
} | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamCanSkip(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
byte[] expected = stream.ToArray(); | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
int skip = 50; | |
int plusOne = 1; | |
int skip2 = bufferSize; | |
// Skip | |
reader.Skip(skip); | |
Assert.Equal(skip, reader.Position); | |
Assert.Equal(stream.Position, reader.Position); | |
// Read | |
Assert.Equal(expected[skip], reader.ReadByte()); | |
// Skip Again | |
reader.Skip(skip2); | |
// First Skip + First Read + Second Skip | |
int position = skip + plusOne + skip2; | |
Assert.Equal(position, reader.Position); | |
Assert.Equal(stream.Position, reader.Position); | |
Assert.Equal(expected[position], reader.ReadByte()); | |
} | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamReadsSmallStream(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
// Create a stream smaller than the default buffer length | |
using (MemoryStream stream = this.CreateTestStream(bufferSize / 4)) | |
{ | |
byte[] expected = stream.ToArray(); | |
const int offset = 5; | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
reader.Position = offset; | |
Assert.Equal(expected[offset], reader.ReadByte()); | |
// We've read a whole length of the stream but increment by 1 in our reader. | |
Assert.Equal(bufferSize / 4, stream.Position); | |
Assert.Equal(offset + 1, reader.Position); | |
} | |
Assert.Equal(offset + 1, stream.Position); | |
} | |
} | |
[Theory] | |
[MemberData(nameof(BufferSizes))] | |
public void BufferedStreamReadsCanReadAllAsSingleByteFromOrigin(int bufferSize) | |
{ | |
this.configuration.StreamProcessingBufferSize = bufferSize; | |
using (MemoryStream stream = this.CreateTestStream(bufferSize * 3)) | |
{ | |
byte[] expected = stream.ToArray(); | |
using (var reader = new BufferedReadStream(this.configuration, stream)) | |
{ | |
for (int i = 0; i < expected.Length; i++) | |
{ | |
Assert.Equal(expected[i], reader.ReadByte()); | |
} | |
} | |
} | |
} | |
private MemoryStream CreateTestStream(int length) | |
{ | |
var buffer = new byte[length]; | |
var random = new Random(); | |
random.NextBytes(buffer); | |
return new EvilStream(buffer); | |
} | |
// Simulates a stream that can only return 1 byte at a time per read instruction. | |
// See https://github.com/SixLabors/ImageSharp/issues/1268 | |
private class EvilStream : MemoryStream | |
{ | |
public EvilStream(byte[] buffer) | |
: base(buffer) | |
{ | |
} | |
public override int Read(byte[] buffer, int offset, int count) | |
{ | |
return base.Read(buffer, offset, 1); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment