|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace System.IO.Pipelines.Tests;
public class BufferWriterTests : IDisposable
{
protected Pipe Pipe;
public BufferWriterTests()
{
Pipe = new Pipe(new PipeOptions(useSynchronizationContext: false, pauseWriterThreshold: 0, resumeWriterThreshold: 0));
}
public void Dispose()
{
Pipe.Writer.Complete();
Pipe.Reader.Complete();
}
private byte[] Read()
{
Pipe.Writer.FlushAsync().GetAwaiter().GetResult();
Pipe.Writer.Complete();
ReadResult readResult = Pipe.Reader.ReadAsync().GetAwaiter().GetResult();
byte[] data = readResult.Buffer.ToArray();
Pipe.Reader.AdvanceTo(readResult.Buffer.End);
return data;
}
[Theory]
[InlineData(3, -1, 0)]
[InlineData(3, 0, -1)]
[InlineData(3, 0, 4)]
[InlineData(3, 4, 0)]
[InlineData(3, -1, -1)]
[InlineData(3, 4, 4)]
public void ThrowsForInvalidParameters(int arrayLength, int offset, int length)
{
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
var array = new byte[arrayLength];
for (var i = 0; i < array.Length; i++)
{
array[i] = (byte)(i + 1);
}
writer.Write(new Span<byte>(array, 0, 0));
writer.Write(new Span<byte>(array, array.Length, 0));
try
{
writer.Write(new Span<byte>(array, offset, length));
Assert.True(false);
}
catch (Exception ex)
{
Assert.True(ex is ArgumentOutOfRangeException);
}
writer.Write(new Span<byte>(array, 0, array.Length));
writer.Commit();
Assert.Equal(array, Read());
}
[Theory]
[InlineData(0, 3)]
[InlineData(1, 2)]
[InlineData(2, 1)]
[InlineData(1, 1)]
public void CanWriteWithOffsetAndLength(int offset, int length)
{
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
var array = new byte[] { 1, 2, 3 };
writer.Write(new Span<byte>(array, offset, length));
Assert.Equal(0, writer.BytesCommitted);
writer.Commit();
Assert.Equal(length, writer.BytesCommitted);
Assert.Equal(array.Skip(offset).Take(length).ToArray(), Read());
Assert.Equal(length, writer.BytesCommitted);
}
[Fact]
public void CanWriteEmpty()
{
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
var array = new byte[] { };
writer.Write(array);
writer.Write(new Span<byte>(array, 0, array.Length));
writer.Commit();
Assert.Equal(0, writer.BytesCommitted);
Assert.Equal(array, Read());
}
[Fact]
public void CanWriteIntoHeadlessBuffer()
{
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
writer.Write(new byte[] { 1, 2, 3 });
writer.Commit();
Assert.Equal(3, writer.BytesCommitted);
Assert.Equal(new byte[] { 1, 2, 3 }, Read());
}
[Fact]
public void CanWriteMultipleTimes()
{
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
writer.Write(new byte[] { 1 });
writer.Write(new byte[] { 2 });
writer.Write(new byte[] { 3 });
writer.Commit();
Assert.Equal(3, writer.BytesCommitted);
Assert.Equal(new byte[] { 1, 2, 3 }, Read());
}
[Fact]
public void CanWriteOverTheBlockLength()
{
Memory<byte> memory = Pipe.Writer.GetMemory();
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
IEnumerable<byte> source = Enumerable.Range(0, memory.Length).Select(i => (byte)i);
byte[] expectedBytes = source.Concat(source).Concat(source).ToArray();
writer.Write(expectedBytes);
writer.Commit();
Assert.Equal(expectedBytes.LongLength, writer.BytesCommitted);
Assert.Equal(expectedBytes, Read());
}
[Fact]
public void EnsureAllocatesSpan()
{
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
writer.Ensure(10);
Assert.True(writer.Span.Length > 10);
Assert.Equal(0, writer.BytesCommitted);
Assert.Equal(new byte[] { }, Read());
}
[Fact]
public void ExposesSpan()
{
int initialLength = Pipe.Writer.GetMemory().Length;
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
Assert.Equal(initialLength, writer.Span.Length);
Assert.Equal(new byte[] { }, Read());
}
[Fact]
public void SlicesSpanAndAdvancesAfterWrite()
{
int initialLength = Pipe.Writer.GetMemory().Length;
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
writer.Write(new byte[] { 1, 2, 3 });
writer.Commit();
Assert.Equal(3, writer.BytesCommitted);
Assert.Equal(initialLength - 3, writer.Span.Length);
Assert.Equal(Pipe.Writer.GetMemory().Length, writer.Span.Length);
Assert.Equal(new byte[] { 1, 2, 3 }, Read());
}
[Fact]
public void BufferWriterCountsBytesCommitted()
{
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
writer.Write(new byte[] { 1, 2, 3 });
Assert.Equal(0, writer.BytesCommitted);
writer.Commit();
Assert.Equal(3, writer.BytesCommitted);
writer.Ensure(10);
writer.Advance(10);
Assert.Equal(3, writer.BytesCommitted);
writer.Commit();
Assert.Equal(13, writer.BytesCommitted);
#pragma warning disable xUnit1031 // Do not use blocking task operations in test method
Pipe.Writer.FlushAsync().GetAwaiter().GetResult();
var readResult = Pipe.Reader.ReadAsync().GetAwaiter().GetResult();
#pragma warning restore xUnit1031 // Do not use blocking task operations in test method
// Consuming the buffer does not change BytesCommitted
Assert.Equal(13, readResult.Buffer.Length);
Assert.Equal(13, writer.BytesCommitted);
}
[Theory]
[InlineData(5)]
[InlineData(50)]
[InlineData(500)]
[InlineData(5000)]
[InlineData(50000)]
public void WriteLargeDataBinary(int length)
{
var data = new byte[length];
new Random(length).NextBytes(data);
BufferWriter<PipeWriter> writer = new BufferWriter<PipeWriter>(Pipe.Writer);
writer.Write(data);
writer.Commit();
Assert.Equal(length, writer.BytesCommitted);
Assert.Equal(data, Read());
}
}
|