aspnetcore/test/Microsoft.AspNet.Mvc.Core.Test/HttpResponseStreamWriterTes...

507 lines
16 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Testing;
using Microsoft.Extensions.MemoryPool;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class HttpResponseStreamWriterTest
{
[Fact]
public async Task DoesNotWriteBOM()
{
// Arrange
var memoryStream = new MemoryStream();
var encodingWithBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true);
var writer = new HttpResponseStreamWriter(memoryStream, encodingWithBOM);
var expectedData = new byte[] { 97, 98, 99, 100 }; // without BOM
// Act
using (writer)
{
await writer.WriteAsync("abcd");
}
// Assert
Assert.Equal(expectedData, memoryStream.ToArray());
}
[Fact]
public async Task DoesNotFlush_UnderlyingStream_OnClosingWriter()
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
await writer.WriteAsync("Hello");
writer.Close();
// Assert
Assert.Equal(0, stream.FlushCallCount);
Assert.Equal(0, stream.FlushAsyncCallCount);
}
[Fact]
public async Task DoesNotFlush_UnderlyingStream_OnDisposingWriter()
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
await writer.WriteAsync("Hello");
writer.Dispose();
// Assert
Assert.Equal(0, stream.FlushCallCount);
Assert.Equal(0, stream.FlushAsyncCallCount);
}
[Fact]
public async Task DoesNotClose_UnderlyingStream_OnDisposingWriter()
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
await writer.WriteAsync("Hello");
writer.Close();
// Assert
Assert.Equal(0, stream.CloseCallCount);
}
[Fact]
public async Task DoesNotDispose_UnderlyingStream_OnDisposingWriter()
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
await writer.WriteAsync("Hello world");
writer.Dispose();
// Assert
Assert.Equal(0, stream.DisposeCallCount);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public async Task FlushesBuffer_OnClose(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
await writer.WriteAsync(new string('a', byteLength));
// Act
writer.Close();
// Assert
Assert.Equal(0, stream.FlushCallCount);
Assert.Equal(0, stream.FlushAsyncCallCount);
Assert.Equal(byteLength, stream.Length);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public async Task FlushesBuffer_OnDispose(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
await writer.WriteAsync(new string('a', byteLength));
// Act
writer.Dispose();
// Assert
Assert.Equal(0, stream.FlushCallCount);
Assert.Equal(0, stream.FlushAsyncCallCount);
Assert.Equal(byteLength, stream.Length);
}
[Fact]
public void NoDataWritten_Flush_DoesNotFlushUnderlyingStream()
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
writer.Flush();
// Assert
Assert.Equal(0, stream.FlushCallCount);
Assert.Equal(0, stream.Length);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public void FlushesBuffer_OnFlush(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
writer.Write(new string('a', byteLength));
// Act
writer.Flush();
// Assert
Assert.Equal(1, stream.FlushCallCount);
Assert.Equal(byteLength, stream.Length);
}
[Fact]
public async Task NoDataWritten_FlushAsync_DoesNotFlushUnderlyingStream()
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
await writer.FlushAsync();
// Assert
Assert.Equal(0, stream.FlushAsyncCallCount);
Assert.Equal(0, stream.Length);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public async Task FlushesBuffer_OnFlushAsync(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
await writer.WriteAsync(new string('a', byteLength));
// Act
await writer.FlushAsync();
// Assert
Assert.Equal(1, stream.FlushAsyncCallCount);
Assert.Equal(byteLength, stream.Length);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public void WriteChar_WritesToStream(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
using (writer)
{
for (var i = 0; i < byteLength; i++)
{
writer.Write('a');
}
}
// Assert
Assert.Equal(byteLength, stream.Length);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public void WriteCharArray_WritesToStream(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
using (writer)
{
writer.Write((new string('a', byteLength)).ToCharArray());
}
// Assert
Assert.Equal(byteLength, stream.Length);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public async Task WriteCharAsync_WritesToStream(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
using (writer)
{
for (var i = 0; i < byteLength; i++)
{
await writer.WriteAsync('a');
}
}
// Assert
Assert.Equal(byteLength, stream.Length);
}
[Theory]
[InlineData(1023)]
[InlineData(1024)]
[InlineData(1050)]
[InlineData(2048)]
public async Task WriteCharArrayAsync_WritesToStream(int byteLength)
{
// Arrange
var stream = new TestMemoryStream();
var writer = new HttpResponseStreamWriter(stream, Encoding.UTF8);
// Act
using (writer)
{
await writer.WriteAsync((new string('a', byteLength)).ToCharArray());
}
// Assert
Assert.Equal(byteLength, stream.Length);
}
[Theory]
[InlineData("你好世界", "utf-16")]
[InlineData("こんにちは世界", "shift_jis")]
[InlineData("హలో ప్రపంచ", "iso-8859-1")]
[InlineData("வணக்கம் உலக", "utf-32")]
public async Task WritesData_InExpectedEncoding(string data, string encodingName)
{
// Arrange
var encoding = Encoding.GetEncoding(encodingName);
var expectedBytes = encoding.GetBytes(data);
var stream = new MemoryStream();
var writer = new HttpResponseStreamWriter(stream, encoding);
// Act
using (writer)
{
await writer.WriteAsync(data);
}
// Assert
Assert.Equal(expectedBytes, stream.ToArray());
}
[Theory]
[InlineData('ん', 1023, "utf-8")]
[InlineData('ん', 1024, "utf-8")]
[InlineData('ん', 1050, "utf-8")]
[InlineData('你', 1023, "utf-16")]
[InlineData('你', 1024, "utf-16")]
[InlineData('你', 1050, "utf-16")]
[InlineData('こ', 1023, "shift_jis")]
[InlineData('こ', 1024, "shift_jis")]
[InlineData('こ', 1050, "shift_jis")]
[InlineData('హ', 1023, "iso-8859-1")]
[InlineData('హ', 1024, "iso-8859-1")]
[InlineData('హ', 1050, "iso-8859-1")]
[InlineData('வ', 1023, "utf-32")]
[InlineData('வ', 1024, "utf-32")]
[InlineData('வ', 1050, "utf-32")]
public async Task WritesData_OfDifferentLength_InExpectedEncoding(
char character,
int charCount,
string encodingName)
{
// Arrange
var encoding = Encoding.GetEncoding(encodingName);
string data = new string(character, charCount);
var expectedBytes = encoding.GetBytes(data);
var stream = new MemoryStream();
var writer = new HttpResponseStreamWriter(stream, encoding);
// Act
using (writer)
{
await writer.WriteAsync(data);
}
// Assert
Assert.Equal(expectedBytes, stream.ToArray());
}
// None of the code in HttpResponseStreamWriter differs significantly when using pooled buffers.
//
// This test effectively verifies that things are correctly constructed and disposed. Pooled buffers
// throw on the finalizer thread if not disposed, so that's why it's complicated.
[Fact]
public void HttpResponseStreamWriter_UsingPooledBuffers()
{
// Arrange
var encoding = Encoding.UTF8;
var stream = new MemoryStream();
var expectedBytes = encoding.GetBytes("Hello, World!");
using (var bytePool = new DefaultArraySegmentPool<byte>())
{
using (var charPool = new DefaultArraySegmentPool<char>())
{
LeasedArraySegment<byte> bytes = null;
LeasedArraySegment<char> chars = null;
HttpResponseStreamWriter writer;
try
{
bytes = bytePool.Lease(4096);
chars = charPool.Lease(1024);
writer = new HttpResponseStreamWriter(stream, encoding, 1024, bytes, chars);
}
catch
{
if (bytes != null)
{
bytes.Owner.Return(bytes);
}
if (chars != null)
{
chars.Owner.Return(chars);
}
throw;
}
// Act
using (writer)
{
writer.Write("Hello, World!");
}
}
}
// Assert
Assert.Equal(expectedBytes, stream.ToArray());
}
// This covers the error case where the byte buffer is too small. This is a safeguard, and shouldn't happen
// if we're using the writer factory.
[Fact]
public void HttpResponseStreamWriter_UsingPooledBuffers_SmallByteBuffer()
{
// Arrange
var encoding = Encoding.UTF8;
var stream = new MemoryStream();
var message =
"The byte buffer must have a length of at least '12291' to be used with a char buffer of " +
"size '4096' and encoding 'Unicode (UTF-8)'. Use 'System.Text.Encoding.GetMaxByteCount' " +
"to compute the correct size for the byte buffer.";
using (var bytePool = new DefaultArraySegmentPool<byte>())
{
using (var charPool = new DefaultArraySegmentPool<char>())
{
LeasedArraySegment<byte> bytes = null;
LeasedArraySegment<char> chars = null;
HttpResponseStreamWriter writer = null;
try
{
bytes = bytePool.Lease(1024);
chars = charPool.Lease(4096);
// Act & Assert
ExceptionAssert.ThrowsArgument(
() => writer = new HttpResponseStreamWriter(stream, encoding, chars.Data.Count, bytes, chars),
"byteBuffer",
message);
writer.Dispose();
}
catch
{
if (bytes != null)
{
bytes.Owner.Return(bytes);
}
if (chars != null)
{
chars.Owner.Return(chars);
}
}
}
}
}
private class TestMemoryStream : MemoryStream
{
private int _flushCallCount;
private int _flushAsyncCallCount;
private int _closeCallCount;
private int _disposeCallCount;
public int FlushCallCount { get { return _flushCallCount; } }
public int FlushAsyncCallCount { get { return _flushAsyncCallCount; } }
public int CloseCallCount { get { return _closeCallCount; } }
public int DisposeCallCount { get { return _disposeCallCount; } }
public override void Flush()
{
_flushCallCount++;
base.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
_flushAsyncCallCount++;
return base.FlushAsync(cancellationToken);
}
public override void Close()
{
_closeCallCount++;
base.Close();
}
protected override void Dispose(bool disposing)
{
_disposeCallCount++;
base.Dispose(disposing);
}
}
}
}