public void Write_WithPartialData() { MemoryStream innerStream = new MemoryStream(); Stream transcodingStream = Encoding.CreateTranscodingStream( innerStream, innerStreamEncoding: CustomAsciiEncoding /* performs custom substitution */, outerStreamEncoding: Encoding.UTF8 /* performs U+FFFD substition */, leaveOpen: true); // First, write some incomplete data transcodingStream.Write(new byte[] { 0x78, 0x79, 0x7A, 0xC3 }); // [C3] shouldn't be flushed yet Assert.Equal("xyz", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // Flushing should have no effect transcodingStream.Flush(); Assert.Equal("xyz", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // Provide the second byte of the multi-byte sequence transcodingStream.WriteByte(0xA0); // [C3 A0] = U+00E0 Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // Provide an incomplete sequence, then close the stream. // Closing the stream should flush the underlying buffers and write the replacement char. transcodingStream.Write(new byte[] { 0xE0, 0xBF }); // first 2 bytes of incomplete 3-byte sequence Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); // wasn't flushed yet transcodingStream.Close(); Assert.Equal("xyz[00E0][FFFD]", ErrorCheckingAsciiEncoding.GetString(innerStream.ToArray())); }
private void RunReadTest(Func <Stream, MemoryStream, int> callback) { MemoryStream sink = new MemoryStream(); MemoryStream innerStream = new MemoryStream(); Stream transcodingStream = Encoding.CreateTranscodingStream(innerStream, innerStreamEncoding: Encoding.UTF8, outerStreamEncoding: CustomAsciiEncoding); // Test with a small string, then test with a large string RunOneTestIteration(128); RunOneTestIteration(10 * 1024 * 1024); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF // Now put some invalid data into the inner stream as EOF. innerStream.SetLength(0); // reset innerStream.WriteByte(0xC0); innerStream.Position = 0; sink.SetLength(0); // reset int numBytesReadJustNow; do { numBytesReadJustNow = callback(transcodingStream, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); Assert.Equal(-1, transcodingStream.ReadByte()); // should've reached EOF void RunOneTestIteration(int stringLength) { sink.SetLength(0); // reset string expectedStringContents = GetVeryLongAsciiString(stringLength); innerStream.SetLength(0); // reset innerStream.Write(Encoding.UTF8.GetBytes(expectedStringContents)); innerStream.Position = 0; int numBytesReadJustNow; do { numBytesReadJustNow = callback(transcodingStream, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal(expectedStringContents, ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); } }
public async Task WriteAsync_WithPartialData() { MemoryStream sink = new MemoryStream(); CancellationToken expectedCancellationToken = new CancellationTokenSource().Token; var innerStreamMock = new Mock <Stream>(MockBehavior.Strict); innerStreamMock.Setup(o => o.CanWrite).Returns(true); innerStreamMock.Setup(o => o.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), expectedCancellationToken)) .Returns <ReadOnlyMemory <byte>, CancellationToken>(sink.WriteAsync); Stream transcodingStream = Encoding.CreateTranscodingStream( innerStreamMock.Object, innerStreamEncoding: CustomAsciiEncoding /* performs custom substitution */, outerStreamEncoding: Encoding.UTF8 /* performs U+FFFD substition */, leaveOpen: true); // First, write some incomplete data await transcodingStream.WriteAsync(new byte[] { 0x78, 0x79, 0x7A, 0xC3 }, expectedCancellationToken); // [C3] shouldn't be flushed yet Assert.Equal("xyz", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); // Provide the second byte of the multi-byte sequence await transcodingStream.WriteAsync(new byte[] { 0xA0 }, expectedCancellationToken); // [C3 A0] = U+00E0 Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); // Provide an incomplete sequence, then close the stream. // Closing the stream should flush the underlying buffers and write the replacement char. await transcodingStream.WriteAsync(new byte[] { 0xE0, 0xBF }, expectedCancellationToken); // first 2 bytes of incomplete 3-byte sequence Assert.Equal("xyz[00E0]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); // wasn't flushed yet // The call to DisposeAsync() will call innerStream.WriteAsync without a CancellationToken. innerStreamMock.Setup(o => o.WriteAsync(It.IsAny <ReadOnlyMemory <byte> >(), CancellationToken.None)) .Returns <ReadOnlyMemory <byte>, CancellationToken>(sink.WriteAsync); await transcodingStream.DisposeAsync(); Assert.Equal("xyz[00E0][FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); }
private async Task RunReadTestAsync(Func <Stream, CancellationToken, MemoryStream, ValueTask <int> > callback, bool suppressExpectedCancellationTokenAsserts = false) { CancellationToken expectedCancellationToken = new CancellationTokenSource().Token; MemoryStream sink = new MemoryStream(); MemoryStream innerStream = new MemoryStream(); var delegatingInnerStreamMock = new Mock <Stream>(MockBehavior.Strict); delegatingInnerStreamMock.Setup(o => o.CanRead).Returns(true); if (suppressExpectedCancellationTokenAsserts) { delegatingInnerStreamMock.Setup(o => o.ReadAsync(It.IsAny <Memory <byte> >(), It.IsAny <CancellationToken>())) .Returns <Memory <byte>, CancellationToken>(innerStream.ReadAsync); } else { delegatingInnerStreamMock.Setup(o => o.ReadAsync(It.IsAny <Memory <byte> >(), expectedCancellationToken)) .Returns <Memory <byte>, CancellationToken>(innerStream.ReadAsync); } Stream transcodingStream = Encoding.CreateTranscodingStream( innerStream: delegatingInnerStreamMock.Object, innerStreamEncoding: Encoding.UTF8, outerStreamEncoding: CustomAsciiEncoding); // Test with a small string, then test with a large string await RunOneTestIteration(128); await RunOneTestIteration(10 * 1024 * 1024); Assert.Equal(-1, await transcodingStream.ReadByteAsync(expectedCancellationToken)); // should've reached EOF // Now put some invalid data into the inner stream as EOF. innerStream.SetLength(0); // reset innerStream.WriteByte(0xC0); innerStream.Position = 0; sink.SetLength(0); // reset int numBytesReadJustNow; do { numBytesReadJustNow = await callback(transcodingStream, expectedCancellationToken, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal("[FFFD]", ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); Assert.Equal(-1, await transcodingStream.ReadByteAsync(expectedCancellationToken)); // should've reached EOF async Task RunOneTestIteration(int stringLength) { sink.SetLength(0); // reset string expectedStringContents = GetVeryLongAsciiString(stringLength); innerStream.SetLength(0); // reset innerStream.Write(Encoding.UTF8.GetBytes(expectedStringContents)); innerStream.Position = 0; int numBytesReadJustNow; do { numBytesReadJustNow = await callback(transcodingStream, expectedCancellationToken, sink); Assert.True(numBytesReadJustNow >= 0); } while (numBytesReadJustNow > 0); Assert.Equal(expectedStringContents, ErrorCheckingAsciiEncoding.GetString(sink.ToArray())); } }