|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
#pragma warning disable IDE0060 // Remove unused parameter
namespace Microsoft.CodeAnalysis.UnitTests
{
public sealed class ObjectSerializationTests
{
[Fact]
public void TestInvalidStreamVersion()
{
var stream = new MemoryStream();
stream.WriteByte(0);
stream.WriteByte(0);
stream.Position = 0;
var reader = ObjectReader.TryGetReader(stream);
Assert.Null(reader);
}
private static void RoundTrip(Action<ObjectWriter> writeAction, Action<ObjectReader> readAction, bool recursive)
{
using var stream = new MemoryStream();
using (var writer = new ObjectWriter(stream, leaveOpen: true))
{
writeAction(writer);
}
stream.Position = 0;
using var reader = ObjectReader.TryGetReader(stream);
readAction(reader);
}
private static void TestRoundTrip(Action<ObjectWriter> writeAction, Action<ObjectReader> readAction)
{
RoundTrip(writeAction, readAction, recursive: true);
RoundTrip(writeAction, readAction, recursive: false);
}
private static T RoundTrip<T>(T value, Action<ObjectWriter, T> writeAction, Func<ObjectReader, T> readAction, bool recursive)
{
using var stream = new MemoryStream();
using (var writer = new ObjectWriter(stream, leaveOpen: true))
{
writeAction(writer, value);
}
stream.Position = 0;
using var reader = ObjectReader.TryGetReader(stream);
return readAction(reader);
}
private static void TestRoundTrip<T>(T value, Action<ObjectWriter, T> writeAction, Func<ObjectReader, T> readAction, bool recursive)
{
var newValue = RoundTrip(value, writeAction, readAction, recursive);
Assert.True(Equalish(value, newValue));
}
private static void TestRoundTrip<T>(T value, Action<ObjectWriter, T> writeAction, Func<ObjectReader, T> readAction)
{
TestRoundTrip(value, writeAction, readAction, recursive: true);
TestRoundTrip(value, writeAction, readAction, recursive: false);
}
private static T RoundTripValue<T>(T value, bool recursive)
{
return RoundTrip(value,
(w, v) =>
{
if (v != null && v.GetType().IsEnum)
{
w.WriteInt64(Convert.ToInt64(v));
}
else
{
w.WriteScalarValue(v);
}
},
r => value != null && value.GetType().IsEnum
? (T)Enum.ToObject(typeof(T), r.ReadInt64())
: (T)r.ReadScalarValue(), recursive);
}
private static void TestRoundTripValue<T>(T value, bool recursive)
{
var newValue = RoundTripValue(value, recursive);
Assert.True(Equalish(value, newValue));
}
private static void TestRoundTripValue<T>(T value)
{
TestRoundTripValue(value, recursive: true);
TestRoundTripValue(value, recursive: false);
}
private static bool Equalish<T>(T value1, T value2)
{
return object.Equals(value1, value2)
|| (value1 is Array && value2 is Array && ArrayEquals((Array)(object)value1, (Array)(object)value2));
}
private static bool ArrayEquals(Array seq1, Array seq2)
{
if (seq1 == null && seq2 == null)
{
return true;
}
else if (seq1 == null || seq2 == null)
{
return false;
}
if (seq1.Length != seq2.Length)
{
return false;
}
for (var i = 0; i < seq1.Length; i++)
{
if (!Equalish(seq1.GetValue(i), seq2.GetValue(i)))
{
return false;
}
}
return true;
}
private class TypeWithOneMember<T> : IEquatable<TypeWithOneMember<T>>
{
private readonly T _member;
public TypeWithOneMember(T value)
{
_member = value;
}
public void WriteTo(ObjectWriter writer)
{
if (typeof(T).IsEnum)
{
writer.WriteInt64(Convert.ToInt64(_member));
}
else
{
writer.WriteScalarValue(_member);
}
}
public override Int32 GetHashCode()
{
if (_member == null)
{
return 0;
}
else
{
return _member.GetHashCode();
}
}
public override Boolean Equals(Object obj)
{
return Equals(obj as TypeWithOneMember<T>);
}
public bool Equals(TypeWithOneMember<T> other)
{
return other != null && Equalish(_member, other._member);
}
}
[Fact]
public void TestValueInt32()
{
TestRoundTripValue(123);
}
[Fact]
public void TestInt32TypeCodes()
{
Assert.Equal(ObjectWriter.TypeCode.Int32_1, ObjectWriter.TypeCode.Int32_0 + 1);
Assert.Equal(ObjectWriter.TypeCode.Int32_2, ObjectWriter.TypeCode.Int32_0 + 2);
Assert.Equal(ObjectWriter.TypeCode.Int32_3, ObjectWriter.TypeCode.Int32_0 + 3);
Assert.Equal(ObjectWriter.TypeCode.Int32_4, ObjectWriter.TypeCode.Int32_0 + 4);
Assert.Equal(ObjectWriter.TypeCode.Int32_5, ObjectWriter.TypeCode.Int32_0 + 5);
Assert.Equal(ObjectWriter.TypeCode.Int32_6, ObjectWriter.TypeCode.Int32_0 + 6);
Assert.Equal(ObjectWriter.TypeCode.Int32_7, ObjectWriter.TypeCode.Int32_0 + 7);
Assert.Equal(ObjectWriter.TypeCode.Int32_8, ObjectWriter.TypeCode.Int32_0 + 8);
Assert.Equal(ObjectWriter.TypeCode.Int32_9, ObjectWriter.TypeCode.Int32_0 + 9);
Assert.Equal(ObjectWriter.TypeCode.Int32_10, ObjectWriter.TypeCode.Int32_0 + 10);
}
[Fact]
public void TestUInt32TypeCodes()
{
Assert.Equal(ObjectWriter.TypeCode.UInt32_1, ObjectWriter.TypeCode.UInt32_0 + 1);
Assert.Equal(ObjectWriter.TypeCode.UInt32_2, ObjectWriter.TypeCode.UInt32_0 + 2);
Assert.Equal(ObjectWriter.TypeCode.UInt32_3, ObjectWriter.TypeCode.UInt32_0 + 3);
Assert.Equal(ObjectWriter.TypeCode.UInt32_4, ObjectWriter.TypeCode.UInt32_0 + 4);
Assert.Equal(ObjectWriter.TypeCode.UInt32_5, ObjectWriter.TypeCode.UInt32_0 + 5);
Assert.Equal(ObjectWriter.TypeCode.UInt32_6, ObjectWriter.TypeCode.UInt32_0 + 6);
Assert.Equal(ObjectWriter.TypeCode.UInt32_7, ObjectWriter.TypeCode.UInt32_0 + 7);
Assert.Equal(ObjectWriter.TypeCode.UInt32_8, ObjectWriter.TypeCode.UInt32_0 + 8);
Assert.Equal(ObjectWriter.TypeCode.UInt32_9, ObjectWriter.TypeCode.UInt32_0 + 9);
Assert.Equal(ObjectWriter.TypeCode.UInt32_10, ObjectWriter.TypeCode.UInt32_0 + 10);
}
private static void TestRoundTripCompressedUint(uint value)
{
TestRoundTrip(value, (w, v) => ((ObjectWriter)w).WriteCompressedUInt(v), r => ((ObjectReader)r).ReadCompressedUInt());
}
[Fact]
public void TestCompressedUInt()
{
TestRoundTripCompressedUint(0);
TestRoundTripCompressedUint(0x01u);
TestRoundTripCompressedUint(0x0123u); // unique bytes tests order
TestRoundTripCompressedUint(0x012345u); // unique bytes tests order
TestRoundTripCompressedUint(0x01234567u); // unique bytes tests order
TestRoundTripCompressedUint(0x3Fu); // largest value packed in one byte
TestRoundTripCompressedUint(0x3FFFu); // largest value packed into two bytes
TestRoundTripCompressedUint(0x3FFFFFu); // no three byte option yet, but test anyway
TestRoundTripCompressedUint(0x3FFFFFFFu); // largest unit allowed in four bytes
Assert.Throws<ArgumentException>(() => TestRoundTripCompressedUint(uint.MaxValue)); // max uint not allowed
Assert.Throws<ArgumentException>(() => TestRoundTripCompressedUint(0x80000000u)); // highest bit set not allowed
Assert.Throws<ArgumentException>(() => TestRoundTripCompressedUint(0x40000000u)); // second highest bit set not allowed
Assert.Throws<ArgumentException>(() => TestRoundTripCompressedUint(0xC0000000u)); // both high bits set not allowed
}
[Theory, CombinatorialData]
public void TestByteSpan([CombinatorialValues(0, 1, 2, 3, 1000, 1000000)] int size)
{
var data = new byte[size];
for (var i = 0; i < data.Length; i++)
{
data[i] = (byte)i;
}
TestRoundTrip(w => TestWritingByteSpan(data, w), r => TestReadingByteSpan(data, r));
}
private static void TestWritingByteSpan(byte[] data, ObjectWriter writer)
{
writer.WriteSpan(data.AsSpan());
}
private static void TestReadingByteSpan(byte[] expected, ObjectReader reader)
{
Assert.True(Enumerable.SequenceEqual(expected, (byte[])reader.ReadByteArray()));
}
private static readonly DateTime _testNow = DateTime.Now;
[Fact]
public void TestPrimitiveValues()
{
TestRoundTripValue(true);
TestRoundTripValue(false);
TestRoundTripValue(Byte.MaxValue);
TestRoundTripValue(SByte.MaxValue);
TestRoundTripValue(Int16.MaxValue);
TestRoundTripValue(Int32.MaxValue);
TestRoundTripValue(Byte.MaxValue);
TestRoundTripValue(Int16.MaxValue);
TestRoundTripValue(Int64.MaxValue);
TestRoundTripValue(UInt16.MaxValue);
TestRoundTripValue(UInt32.MaxValue);
TestRoundTripValue(UInt64.MaxValue);
TestRoundTripValue(Decimal.MaxValue);
TestRoundTripValue(Double.MaxValue);
TestRoundTripValue(Single.MaxValue);
TestRoundTripValue('X');
TestRoundTripValue("YYY");
TestRoundTripValue("\uD800\uDC00"); // valid surrogate pair
TestRoundTripValue("\uDC00\uD800"); // invalid surrogate pair
TestRoundTripValue("\uD800"); // incomplete surrogate pair
TestRoundTripValue<object>(null);
TestRoundTripValue(ConsoleColor.Cyan);
TestRoundTripValue(EByte.Value);
TestRoundTripValue(ESByte.Value);
TestRoundTripValue(EShort.Value);
TestRoundTripValue(EUShort.Value);
TestRoundTripValue(EInt.Value);
TestRoundTripValue(EUInt.Value);
TestRoundTripValue(ELong.Value);
TestRoundTripValue(EULong.Value);
TestRoundTripValue(_testNow);
}
[Fact]
public void TestInt32Values()
{
TestRoundTripValue<Int32>(0);
TestRoundTripValue<Int32>(1);
TestRoundTripValue<Int32>(2);
TestRoundTripValue<Int32>(3);
TestRoundTripValue<Int32>(4);
TestRoundTripValue<Int32>(5);
TestRoundTripValue<Int32>(6);
TestRoundTripValue<Int32>(7);
TestRoundTripValue<Int32>(8);
TestRoundTripValue<Int32>(9);
TestRoundTripValue<Int32>(10);
TestRoundTripValue<Int32>(-1);
TestRoundTripValue<Int32>(Int32.MinValue);
TestRoundTripValue<Int32>(Byte.MaxValue);
TestRoundTripValue<Int32>(UInt16.MaxValue);
TestRoundTripValue<Int32>(Int32.MaxValue);
}
[Fact]
public void TestUInt32Values()
{
TestRoundTripValue<UInt32>(0);
TestRoundTripValue<UInt32>(1);
TestRoundTripValue<UInt32>(2);
TestRoundTripValue<UInt32>(3);
TestRoundTripValue<UInt32>(4);
TestRoundTripValue<UInt32>(5);
TestRoundTripValue<UInt32>(6);
TestRoundTripValue<UInt32>(7);
TestRoundTripValue<UInt32>(8);
TestRoundTripValue<UInt32>(9);
TestRoundTripValue<UInt32>(10);
TestRoundTripValue<Int32>(Byte.MaxValue);
TestRoundTripValue<Int32>(UInt16.MaxValue);
TestRoundTripValue<Int32>(Int32.MaxValue);
}
[Fact]
public void TestInt64Values()
{
TestRoundTripValue<Int64>(0);
TestRoundTripValue<Int64>(1);
TestRoundTripValue<Int64>(2);
TestRoundTripValue<Int64>(3);
TestRoundTripValue<Int64>(4);
TestRoundTripValue<Int64>(5);
TestRoundTripValue<Int64>(6);
TestRoundTripValue<Int64>(7);
TestRoundTripValue<Int64>(8);
TestRoundTripValue<Int64>(9);
TestRoundTripValue<Int64>(10);
TestRoundTripValue<Int64>(-1);
TestRoundTripValue<Int64>(Byte.MinValue);
TestRoundTripValue<Int64>(Byte.MaxValue);
TestRoundTripValue<Int64>(Int16.MinValue);
TestRoundTripValue<Int64>(Int16.MaxValue);
TestRoundTripValue<Int64>(UInt16.MinValue);
TestRoundTripValue<Int64>(UInt16.MaxValue);
TestRoundTripValue<Int64>(Int32.MinValue);
TestRoundTripValue<Int64>(Int32.MaxValue);
TestRoundTripValue<Int64>(UInt32.MinValue);
TestRoundTripValue<Int64>(UInt32.MaxValue);
TestRoundTripValue<Int64>(Int64.MinValue);
TestRoundTripValue<Int64>(Int64.MaxValue);
}
[Fact]
public void TestUInt64Values()
{
TestRoundTripValue<UInt64>(0);
TestRoundTripValue<UInt64>(1);
TestRoundTripValue<UInt64>(2);
TestRoundTripValue<UInt64>(3);
TestRoundTripValue<UInt64>(4);
TestRoundTripValue<UInt64>(5);
TestRoundTripValue<UInt64>(6);
TestRoundTripValue<UInt64>(7);
TestRoundTripValue<UInt64>(8);
TestRoundTripValue<UInt64>(9);
TestRoundTripValue<UInt64>(10);
TestRoundTripValue<UInt64>(Byte.MinValue);
TestRoundTripValue<UInt64>(Byte.MaxValue);
TestRoundTripValue<UInt64>(UInt16.MinValue);
TestRoundTripValue<UInt64>(UInt16.MaxValue);
TestRoundTripValue<UInt64>(Int32.MaxValue);
TestRoundTripValue<UInt64>(UInt32.MinValue);
TestRoundTripValue<UInt64>(UInt32.MaxValue);
TestRoundTripValue<UInt64>(UInt64.MinValue);
TestRoundTripValue<UInt64>(UInt64.MaxValue);
}
[Fact]
public void TestPrimitiveAPIs()
{
TestRoundTrip(w => TestWritingPrimitiveAPIs(w), r => TestReadingPrimitiveAPIs(r));
}
private static void TestWritingPrimitiveAPIs(ObjectWriter writer)
{
writer.WriteBoolean(true);
writer.WriteBoolean(false);
writer.WriteByte(Byte.MaxValue);
writer.WriteSByte(SByte.MaxValue);
writer.WriteInt16(Int16.MaxValue);
writer.WriteInt32(Int32.MaxValue);
writer.WriteInt32(Byte.MaxValue);
writer.WriteInt32(Int16.MaxValue);
writer.WriteInt64(Int64.MaxValue);
writer.WriteUInt16(UInt16.MaxValue);
writer.WriteUInt32(UInt32.MaxValue);
writer.WriteUInt64(UInt64.MaxValue);
writer.WriteDecimal(Decimal.MaxValue);
writer.WriteDouble(Double.MaxValue);
writer.WriteSingle(Single.MaxValue);
writer.WriteChar('X');
writer.WriteString("YYY");
writer.WriteString("\uD800\uDC00"); // valid surrogate pair
writer.WriteString("\uDC00\uD800"); // invalid surrogate pair
writer.WriteString("\uD800"); // incomplete surrogate pair
}
private static void TestReadingPrimitiveAPIs(ObjectReader reader)
{
Assert.True(reader.ReadBoolean());
Assert.False(reader.ReadBoolean());
Assert.Equal(Byte.MaxValue, reader.ReadByte());
Assert.Equal(SByte.MaxValue, reader.ReadSByte());
Assert.Equal(Int16.MaxValue, reader.ReadInt16());
Assert.Equal(Int32.MaxValue, reader.ReadInt32());
Assert.Equal(Byte.MaxValue, reader.ReadInt32());
Assert.Equal(Int16.MaxValue, reader.ReadInt32());
Assert.Equal(Int64.MaxValue, reader.ReadInt64());
Assert.Equal(UInt16.MaxValue, reader.ReadUInt16());
Assert.Equal(UInt32.MaxValue, reader.ReadUInt32());
Assert.Equal(UInt64.MaxValue, reader.ReadUInt64());
Assert.Equal(Decimal.MaxValue, reader.ReadDecimal());
Assert.Equal(Double.MaxValue, reader.ReadDouble());
Assert.Equal(Single.MaxValue, reader.ReadSingle());
Assert.Equal('X', reader.ReadChar());
Assert.Equal("YYY", reader.ReadString());
Assert.Equal("\uD800\uDC00", reader.ReadString()); // valid surrogate pair
Assert.Equal("\uDC00\uD800", reader.ReadString()); // invalid surrogate pair
Assert.Equal("\uD800", reader.ReadString()); // incomplete surrogate pair
}
[Fact]
public void TestPrimitivesValue()
{
TestRoundTrip(w => TestWritingPrimitiveValues(w), r => TestReadingPrimitiveValues(r));
}
private static void TestWritingPrimitiveValues(ObjectWriter writer)
{
writer.WriteScalarValue(true);
writer.WriteScalarValue(false);
writer.WriteScalarValue(Byte.MaxValue);
writer.WriteScalarValue(SByte.MaxValue);
writer.WriteScalarValue(Int16.MaxValue);
writer.WriteScalarValue(Int32.MaxValue);
writer.WriteScalarValue((Int32)Byte.MaxValue);
writer.WriteScalarValue((Int32)Int16.MaxValue);
writer.WriteScalarValue(Int64.MaxValue);
writer.WriteScalarValue(UInt16.MaxValue);
writer.WriteScalarValue(UInt32.MaxValue);
writer.WriteScalarValue(UInt64.MaxValue);
writer.WriteScalarValue(Decimal.MaxValue);
writer.WriteScalarValue(Double.MaxValue);
writer.WriteScalarValue(Single.MaxValue);
writer.WriteScalarValue('X');
writer.WriteScalarValue((object)"YYY");
writer.WriteScalarValue((object)"\uD800\uDC00"); // valid surrogate pair
writer.WriteScalarValue((object)"\uDC00\uD800"); // invalid surrogate pair
writer.WriteScalarValue((object)"\uD800"); // incomplete surrogate pair
writer.WriteScalarValue((object)null);
unchecked
{
writer.WriteInt64((long)ConsoleColor.Cyan);
writer.WriteInt64((long)EByte.Value);
writer.WriteInt64((long)ESByte.Value);
writer.WriteInt64((long)EShort.Value);
writer.WriteInt64((long)EUShort.Value);
writer.WriteInt64((long)EInt.Value);
writer.WriteInt64((long)EUInt.Value);
writer.WriteInt64((long)ELong.Value);
writer.WriteInt64((long)EULong.Value);
}
writer.WriteScalarValue(_testNow);
}
private static void TestReadingPrimitiveValues(ObjectReader reader)
{
Assert.True((bool)reader.ReadScalarValue());
Assert.False((bool)reader.ReadScalarValue());
Assert.Equal(Byte.MaxValue, (Byte)reader.ReadScalarValue());
Assert.Equal(SByte.MaxValue, (SByte)reader.ReadScalarValue());
Assert.Equal(Int16.MaxValue, (Int16)reader.ReadScalarValue());
Assert.Equal(Int32.MaxValue, (Int32)reader.ReadScalarValue());
Assert.Equal(Byte.MaxValue, (Int32)reader.ReadScalarValue());
Assert.Equal(Int16.MaxValue, (Int32)reader.ReadScalarValue());
Assert.Equal(Int64.MaxValue, (Int64)reader.ReadScalarValue());
Assert.Equal(UInt16.MaxValue, (UInt16)reader.ReadScalarValue());
Assert.Equal(UInt32.MaxValue, (UInt32)reader.ReadScalarValue());
Assert.Equal(UInt64.MaxValue, (UInt64)reader.ReadScalarValue());
Assert.Equal(Decimal.MaxValue, (Decimal)reader.ReadScalarValue());
Assert.Equal(Double.MaxValue, (Double)reader.ReadScalarValue());
Assert.Equal(Single.MaxValue, (Single)reader.ReadScalarValue());
Assert.Equal('X', (Char)reader.ReadScalarValue());
Assert.Equal("YYY", (String)reader.ReadScalarValue());
Assert.Equal("\uD800\uDC00", (String)reader.ReadScalarValue()); // valid surrogate pair
Assert.Equal("\uDC00\uD800", (String)reader.ReadScalarValue()); // invalid surrogate pair
Assert.Equal("\uD800", (String)reader.ReadScalarValue()); // incomplete surrogate pair
Assert.Null(reader.ReadScalarValue());
unchecked
{
Assert.Equal((long)ConsoleColor.Cyan, reader.ReadInt64());
Assert.Equal((long)EByte.Value, reader.ReadInt64());
Assert.Equal((long)ESByte.Value, reader.ReadInt64());
Assert.Equal((long)EShort.Value, reader.ReadInt64());
Assert.Equal((long)EUShort.Value, reader.ReadInt64());
Assert.Equal((long)EInt.Value, reader.ReadInt64());
Assert.Equal((long)EUInt.Value, reader.ReadInt64());
Assert.Equal((long)ELong.Value, reader.ReadInt64());
Assert.Equal((long)EULong.Value, reader.ReadInt64());
}
Assert.Equal(_testNow, (DateTime)reader.ReadScalarValue());
}
public enum EByte : byte
{
Value = 1
}
public enum ESByte : sbyte
{
Value = 2
}
public enum EShort : short
{
Value = 3
}
public enum EUShort : ushort
{
Value = 4
}
public enum EInt : int
{
Value = 5
}
public enum EUInt : uint
{
Value = 6
}
public enum ELong : long
{
Value = 7
}
public enum EULong : ulong
{
Value = 8
}
[Fact]
public void TestRoundTripCharacters()
{
// round trip all possible characters as a string
for (int i = ushort.MinValue; i <= ushort.MaxValue; i++)
{
TestRoundTripChar((char)i);
}
}
private static void TestRoundTripChar(Char ch)
{
TestRoundTrip(ch, (w, v) => w.WriteChar(v), r => r.ReadChar());
}
[Fact]
public void TestRoundTripGuid()
{
static void test(Guid guid)
{
TestRoundTrip(guid, (w, v) => w.WriteGuid(v), r => r.ReadGuid());
}
test(Guid.Empty);
test(new Guid(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1));
test(new Guid(0b10000000000000000000000000000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1));
test(new Guid(0b10000000000000000000000000000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
for (var i = 0; i < 10; i++)
{
test(Guid.NewGuid());
}
}
[Fact]
public void TestRoundTripStringCharacters()
{
// round trip all possible characters as a string
for (int i = ushort.MinValue; i <= ushort.MaxValue; i++)
{
TestRoundTripStringCharacter((ushort)i);
}
// round trip single string with all possible characters
var sb = new StringBuilder();
for (int i = ushort.MinValue; i <= ushort.MaxValue; i++)
{
sb.Append((char)i);
}
TestRoundTripString(sb.ToString());
}
private static void TestRoundTripString(string text)
{
TestRoundTrip(text, (w, v) => w.WriteString(v), r => r.ReadString());
}
private static void TestRoundTripStringCharacter(ushort code)
{
TestRoundTripString(new String((char)code, 1));
}
private static void TestRoundTripArray<T>(T[] values)
{
TestRoundTripValue(values);
}
public static IEnumerable<object[]> GetEncodingTestCases()
=> EncodingTestHelpers.GetEncodingTestCases();
[Theory]
[MemberData(nameof(GetEncodingTestCases))]
public void Encodings(Encoding original)
{
using var stream = new MemoryStream();
using (var writer = new ObjectWriter(stream, leaveOpen: true))
{
writer.WriteEncoding(original);
}
stream.Position = 0;
using var reader = ObjectReader.TryGetReader(stream);
Assert.NotNull(reader);
var deserialized = reader.ReadEncoding();
EncodingTestHelpers.AssertEncodingsEqual(original, deserialized);
}
[Fact]
public void TestMultipleAssetWritingAndReader()
{
using var stream = new MemoryStream();
const string GooString = "Goo";
const string BarString = "Bar";
var largeString = new string('a', 1024);
// Write out some initial bytes, to demonstrate the reader not throwing, even if we don't have the right
// validation bytes at the start.
stream.WriteByte(1);
stream.WriteByte(2);
using (var writer = new ObjectWriter(stream, leaveOpen: true, writeValidationBytes: false))
{
writer.WriteValidationBytes();
writer.WriteString(GooString);
writer.WriteString("Bar");
writer.WriteString(largeString);
// Random data, not going through the writer.
stream.WriteByte(3);
stream.WriteByte(4);
// We should be able to write out a new object, using strings we've already seen.
writer.WriteValidationBytes();
writer.WriteString(largeString);
writer.WriteString("Bar");
writer.WriteString(GooString);
}
stream.Position = 0;
using var reader = ObjectReader.GetReader(stream, leaveOpen: true, checkValidationBytes: false);
Assert.Equal(1, reader.ReadByte());
Assert.Equal(2, reader.ReadByte());
reader.CheckValidationBytes();
var string1 = reader.ReadString();
var string2 = reader.ReadString();
var string3 = reader.ReadString();
Assert.Equal(GooString, string1);
Assert.Equal(BarString, string2);
Assert.Equal(largeString, string3);
Assert.NotSame(GooString, string1);
Assert.NotSame(BarString, string2);
Assert.NotSame(largeString, string3);
Assert.Equal(3, stream.ReadByte());
Assert.Equal(4, stream.ReadByte());
reader.CheckValidationBytes();
var string4 = reader.ReadString();
var string5 = reader.ReadString();
var string6 = reader.ReadString();
Assert.Equal(largeString, string4);
Assert.Equal(BarString, string5);
Assert.Equal(GooString, string6);
Assert.NotSame(largeString, string4);
Assert.NotSame(BarString, string5);
Assert.NotSame(GooString, string6);
// These should be references to the same strings in the format string and should return the values already
// returned.
Assert.Same(string1, string6);
Assert.Same(string2, string5);
Assert.Same(string3, string4);
}
// keep these around for analyzing perf issues
#if false
[Fact]
public void TestReaderPerf()
{
var iterations = 10000;
var recTime = TestReaderPerf(iterations, recursive: true);
var nonTime = TestReaderPerf(iterations, recursive: false);
Console.WriteLine($"Recursive Time : {recTime.TotalMilliseconds}");
Console.WriteLine($"Non Recursive Time: {nonTime.TotalMilliseconds}");
}
[Fact]
public void TestNonRecursiveReaderPerf()
{
var iterations = 10000;
var nonTime = TestReaderPerf(iterations, recursive: false);
}
private TimeSpan TestReaderPerf(int iterations, bool recursive)
{
int id = 0;
var graph = ConstructGraph(ref id, 5, 3);
var stream = new MemoryStream();
var binder = new RecordingObjectBinder();
var writer = new StreamObjectWriter(stream, binder: binder, recursive: recursive);
writer.WriteValue(graph);
writer.Dispose();
var start = DateTime.Now;
for (int i = 0; i < iterations; i++)
{
stream.Position = 0;
var reader = new StreamObjectReader(stream, binder: binder);
var item = reader.ReadValue();
}
return DateTime.Now - start;
}
#endif
}
}
#pragma warning restore IDE0060 // Remove unused parameter
|