Files
simulation_core/lib/Migrant/Tests/SerializationTests.cs

1775 lines
53 KiB
C#

//
// Copyright (c) 2012-2021 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in the LICENSE file.
using System;
using System.Threading;
using NUnit.Framework;
using System.Collections.Generic;
using System.Collections;
using System.Collections.Concurrent;
using System.Linq;
using System.IO;
using System.Collections.ObjectModel;
using Antmicro.Migrant.Hooks;
namespace Antmicro.Migrant.Tests
{
[TestFixture(false, false, false, false, true)]
[TestFixture(true, false, false, false, true)]
[TestFixture(false, true, false, false, true)]
[TestFixture(true, true, false, false, true)]
[TestFixture(false, false, true, false, true)]
[TestFixture(true, false, true, false, true)]
[TestFixture(false, true, true, false, true)]
[TestFixture(true, true, true, false, true)]
[TestFixture(false, false, false, true, true)]
[TestFixture(true, false, false, true, true)]
[TestFixture(false, true, false, true, true)]
[TestFixture(true, true, false, true, true)]
[TestFixture(false, false, true, true, true)]
[TestFixture(true, false, true, true, true)]
[TestFixture(false, true, true, true, true)]
[TestFixture(true, true, true, true, true)]
[TestFixture(false, false, false, false, false)]
[TestFixture(true, false, false, false, false)]
[TestFixture(false, true, false, false, false)]
[TestFixture(true, true, false, false, false)]
[TestFixture(false, false, true, false, false)]
[TestFixture(true, false, true, false, false)]
[TestFixture(false, true, true, false, false)]
[TestFixture(true, true, true, false, false)]
[TestFixture(false, false, false, true, false)]
[TestFixture(true, false, false, true, false)]
[TestFixture(false, true, false, true, false)]
[TestFixture(true, true, false, true, false)]
[TestFixture(false, false, true, true, false)]
[TestFixture(true, false, true, true, false)]
[TestFixture(false, true, true, true, false)]
[TestFixture(true, true, true, true, false)]
public class SerializationTests : BaseTestWithSettings
{
public SerializationTests(bool useGeneratedSerializer, bool useGeneratedDeserializer, bool treatCollectionsAsUserObjects, bool supportForISerializable, bool useTypeStamping) : base(useGeneratedSerializer, useGeneratedDeserializer, treatCollectionsAsUserObjects, supportForISerializable, false, useTypeStamping, true)
{
useGeneratedSerialization = useGeneratedSerializer;
}
[Test]
public void ShouldSerializeObject()
{
var obj = new object();
var copy = SerializerClone(obj);
Assert.AreEqual(copy.GetType(), typeof(object));
}
[Test]
public void ShouldSerializeSimpleClass()
{
var simpleClass = new SimpleClass { Value = 0x69, Str = "Magic" };
var copy = SerializerClone(simpleClass);
Assert.AreEqual(simpleClass, copy);
}
[Test]
public void ShouldSerializeCyclicReference()
{
var one = new CyclicRef();
var another = new CyclicRef { Another = one, Val = 1 };
one.Another = another;
var oneCopy = SerializerClone(one);
Assert.AreEqual(oneCopy, oneCopy.Another.Another, "Cyclic reference was not properly serialized.");
}
[Test]
public void ShouldSerializeReferenceToSelf()
{
var one = new CyclicRef();
one.Another = one;
var copy = SerializerClone(one);
Assert.AreSame(copy, copy.Another);
}
[Test]
public void ShouldSerializeArrayReferenceToSelf()
{
var array = new object[2];
array[0] = array;
array[1] = array;
var copy = SerializerClone(array);
Assert.AreSame(copy, copy[0]);
Assert.AreSame(copy[0], copy[1]);
}
[Test]
public void ShouldSerializeNullReference()
{
var simpleClass = new SimpleClass();
var copy = SerializerClone(simpleClass);
Assert.AreEqual(simpleClass, copy);
}
[Test]
public void ShouldPreserveIdentity()
{
var str = "This is a string";
var simple1 = new SimpleClass { Str = str };
var simple2 = new SimpleClass { Str = str };
var container = new SimpleContainer { First = simple1, Second = simple2 };
var copy = SerializerClone(container);
Assert.AreSame(copy.First.Str, copy.Second.Str);
}
[Test]
public void ShouldSerializeReadonlyField()
{
const int testValue = 0xABCD;
var readonlyClass = new ClassWithReadonly(testValue);
var copy = SerializerClone(readonlyClass);
Assert.AreEqual(readonlyClass.ReadonlyValue, copy.ReadonlyValue);
}
[Test]
public void ShouldNotSerializeIntPtr()
{
var intPtrClass = new ClassWithIntPtr{ Ptr = new IntPtr(0x666) };
try
{
SerializerClone(intPtrClass);
Assert.Fail("Class with IntPtr was serialized without exception.");
}
catch(InvalidOperationException)
{
}
}
[Test]
public void ShouldNotSerializeSpinLock()
{
var spinLockClass = new ClassWithSpinLock{ SLock = new SpinLock() };
try
{
SerializerClone(spinLockClass);
Assert.Fail("Class with SpinLock was serialized without exception.");
}
catch(InvalidOperationException)
{
}
}
[Test]
public void ShouldSerializeWithInheritance()
{
SimpleBaseClass derived = new SimpleDerivedClass { BaseField = 1, DerivedField = 2 };
var copy = (SimpleDerivedClass)SerializerClone(derived);
Assert.AreEqual(copy.DerivedField, 2);
Assert.AreEqual(copy.BaseField, 1);
}
[Test]
public void ShouldSerializeBoxWithLazyScan()
{
var str = "Something";
var box = new Box { Element = str };
var copy = SerializerClone(box);
Assert.AreEqual(str, copy.Element);
}
[Test]
public void ShouldSerializeFieldWithDifferentFormalAndActualType()
{
var box = new Box();
var anotherBox = new Box { Element = box };
var copy = SerializerClone(anotherBox);
Assert.AreEqual(typeof(Box), copy.Element.GetType());
}
[Test]
public void ShouldSerializeListOfPrimitives()
{
var list = new List<int> { 1, 2, 3 };
var copy = SerializerClone(list);
CollectionAssert.AreEqual(list, copy);
}
[Test]
public void ShouldSerializeLongListOfPrimitives()
{
var list = new List<int>();
for(var i = 0; i < 100000; i++)
{
list.Add(i);
}
var copy = SerializerClone(list);
CollectionAssert.AreEqual(list, copy);
}
[Test]
public void ShouldSerializeListOfLists()
{
var first = new List<int> { 1, 2, 3 };
var second = new List<int> { 97, 98, 99 };
var list = new List<List<int>> { first, second };
var copy = SerializerClone(list);
for(var i = 0; i < 2; i++)
{
CollectionAssert.AreEqual(list[i], copy[i]);
}
}
[Test]
public void ShouldPreserveIdentityOnLists()
{
var list = new List<Box>();
var box1 = new Box { Element = list };
var box2 = new Box { Element = list };
list.Add(box1);
list.Add(box2);
var copy = SerializerClone(list);
Assert.AreSame(copy, copy[0].Element);
Assert.AreSame(copy[0].Element, copy[1].Element);
}
[Test]
public void ShouldSerializeArrayListWithStrings()
{
var list = new ArrayList { "Word 1", "Word 2", new SimpleClass { Value = 6, Str = "Word 4" }, "Word 3" };
var copy = SerializerClone(list);
CollectionAssert.AreEqual(list, copy);
}
[Test]
public void ShouldSerializeArrayListWithPrimitives()
{
var list = new ArrayList { 1, 2, 3 };
var copy = SerializerClone(list);
CollectionAssert.AreEqual(list, copy);
}
[Test]
public void ShouldSerializeHashSetWithPrimitives()
{
var collection = new HashSet<int> { 1, 2, 3 };
var copy = SerializerClone(collection);
CollectionAssert.AreEquivalent(collection, copy);
}
[Test]
public void ShouldSerializeReadOnlyCollection()
{
var list = new List<object> { "asdasd", 1, "cvzxcv" };
var roList = new ReadOnlyCollection<object>(list);
var copy = SerializerClone(roList);
CollectionAssert.AreEqual(roList, copy);
}
[Test]
public void ShouldSerializeBoxedPrimitve()
{
var box = new Box { Element = 3 };
var copy = SerializerClone(box);
Assert.AreEqual(3, copy.Element);
}
[Test]
public void ShouldPreserveIdentityWithBoxedPrimitives()
{
object primitive = 1234;
var box1 = new Box { Element = primitive };
var box2 = new Box { Element = primitive };
var list = new List<Box> { box1, box2 };
var copy = SerializerClone(list);
Assert.AreSame(copy[0].Element, copy[1].Element);
}
[Test]
public void ShouldSerializeListWithNull()
{
var list = new List<object> { "One", null, "Two" };
var copy = SerializerClone(list);
CollectionAssert.AreEqual(list, copy);
}
[Test]
public void ShouldSerializeArrayWithPrimitives()
{
var array = new [] { 1, 2, 3, 4, 5, 6 };
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array, copy);
}
[Test]
public void ShouldSerializeTwodimensionalArrayWithPrimitives()
{
var array = new [,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array, copy);
}
[Test]
public void ShouldSerializeArrayWithStrings()
{
var array = new [] { "One", "Two", "Three" };
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array, copy);
}
[Test]
public void ShouldSerializeTwodimensionalArrayWithStrings()
{
var array = new [,] { { "One", "Two" }, { "Three", "Four" } };
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array, copy);
}
[Test]
public void ShouldSerialize4DArrayWithPrimitives()
{
var array = new int[2, 2, 3, 4];
array[0, 0, 0, 0] = 1;
array[1, 0, 1, 0] = 2;
array[0, 1, 0, 1] = 3;
array[0, 1, 2, 2] = 4;
array[1, 0, 2, 3] = 5;
var copy = SerializerClone(array);
Assert.AreEqual(array, copy);
}
[Test]
public void ShouldSerializeMixedArray()
{
var obj = new object();
var array = new [] { "One", obj, "Two", obj, new object() };
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array.Where(x => x is String), copy.Where(x => x is String));
Assert.AreSame(copy[1], copy[3]);
Assert.AreNotSame(copy[3], copy[4]);
}
[Test]
public void ShouldSerializeDictionaryWithPrimitives()
{
var dictionary = new Dictionary<int, int>();
for(var i = 0; i < 100; i++)
{
dictionary.Add(i, i + 1);
}
var copy = SerializerClone(dictionary);
CollectionAssert.AreEquivalent(dictionary, copy);
}
[Test]
public void ShouldSerializeHashtable()
{
var hashtable = new Hashtable();
for(var i = 0; i < 100; i++)
{
hashtable.Add(i, i + 1);
}
var copy = SerializerClone(hashtable);
CollectionAssert.AreEquivalent(hashtable, copy);
}
[Test]
public void ShouldSerializeDictionaryWithStrings()
{
var dictionary = new Dictionary<string, string> {
{ "Ball", "Piłka" },
{ "Cat", "Kot" },
{ "Line", "Linia" }
};
var copy = SerializerClone(dictionary);
CollectionAssert.AreEquivalent(dictionary, copy);
}
[Test]
public void ShouldPreserveIdentityWithDictionary()
{
var str = "Word";
var dictionary = new Dictionary<int, string> {
{ 10, str },
{ 20, "One" },
{ 30, str }
};
var copy = SerializerClone(dictionary);
CollectionAssert.AreEquivalent(dictionary, copy);
Assert.AreSame(copy[10], copy[30]);
Assert.AreNotSame(copy[10], copy[20]);
}
[Test]
public void ShouldSerializeSimpleEnum()
{
var e = SimpleEnum.Two;
var copy = SerializerClone(e);
Assert.AreEqual(e, copy);
}
[Test]
public void ShouldSerializeEnums()
{
var enumLongValues = Enum.GetValues(typeof(EnumLong)).Cast<EnumLong>().ToArray();
var enumShortValues = Enum.GetValues(typeof(EnumShort)).Cast<EnumShort>().ToArray();
var enumFlagsValues = Enum.GetValues(typeof(TestEnumFlags)).Cast<TestEnumFlags>()
.Union(new [] {
TestEnumFlags.First | TestEnumFlags.Second
, TestEnumFlags.First | TestEnumFlags.Second | TestEnumFlags.Third
, TestEnumFlags.Second | TestEnumFlags.Third
}).ToArray();
foreach(var item in enumLongValues)
{
Assert.AreEqual(item, SerializerClone(item));
}
foreach(var item in enumShortValues)
{
Assert.AreEqual(item, SerializerClone(item));
}
foreach(var item in enumFlagsValues)
{
Assert.AreEqual(item, SerializerClone(item));
}
}
[Test]
public void ShouldSerializeSimplerStruct()
{
var str = new SimplerStruct { A = 1234567, B = 543 };
var copy = SerializerClone(str);
Assert.AreEqual(str, copy);
}
[Test]
public void ShouldSerializeSimpleStruct()
{
var str = new SimpleStruct {
A = 5,
B = "allman"
};
var copy = SerializerClone(str);
Assert.AreEqual(str, copy);
}
[Test]
public void ShouldSerializeComplexStruct()
{
var str = new SimpleStruct {
A = 5,
B = "allman"
};
var compstr = new ComplexStruct {
A = 6,
B = str
};
var newStr = SerializerClone(compstr);
Assert.AreEqual(compstr, newStr);
}
[Test]
public void ShouldSerializeStructInClass()
{
var str = new SimpleStruct { A = 1, B = "allman" };
var box = new GenericBox<SimpleStruct> { Element = str };
var copy = SerializerClone(box);
Assert.AreEqual(str, copy.Element);
}
[Test]
public void ShouldSerializeSpeciallySerializable()
{
var special = new SpeciallySerializable();
special.Fill(100);
var copy = SerializerClone(special);
CollectionAssert.AreEqual(special.Data, copy.Data);
}
[Test]
public void ShouldSerializeSpeciallySerializableInArrayAndPreserveIdentity()
{
var special = new SpeciallySerializable();
special.Fill(100);
var str = "Some string";
var box = new Box { Element = special };
var anotherSpecial = new SpeciallySerializable();
anotherSpecial.Fill(100);
var array = new object[] { special, str, box, anotherSpecial };
var copy = SerializerClone(array);
Assert.AreSame(copy[0], ((Box)copy[2]).Element);
Assert.AreNotSame(copy[0], copy[3]);
CollectionAssert.AreEqual(((SpeciallySerializable)copy[0]).Data, special.Data);
CollectionAssert.AreEqual(((SpeciallySerializable)copy[3]).Data, anotherSpecial.Data);
}
[Test]
public void ShouldSerializeBoxWithLong()
{
var box = new GenericBox<long> { Element = 1234 };
var copy = SerializerClone(box);
Assert.AreEqual(box.Element, copy.Element);
}
[Test]
public void ShouldSerializeStructWithSpeciallySerializable()
{
var special = new SpeciallySerializable();
special.Fill(100);
var box = new StructGenericBox<SpeciallySerializable> { Element = special };
var copy = SerializerClone(box);
Assert.AreEqual(box.Element.Data, copy.Element.Data);
}
[Test]
public void ShouldSerializeNonNullNullable()
{
var box = new GenericBox<int?> { Element = 3 };
var copy = SerializerClone(box);
Assert.AreEqual(box.Element, copy.Element);
}
[Test]
public void ShouldSerializeNullNullable()
{
var box = new GenericBox<int?> { Element = null };
var copy = SerializerClone(box);
Assert.AreEqual(box.Element, copy.Element);
}
[Test]
public void ShouldSerializeBoxedNullNullable()
{
int? value = 66;
var box = new Box { Element = value };
var copy = SerializerClone(box);
Assert.AreEqual(box.Element, copy.Element);
}
[Test]
public void ShouldSerializeBoxedNonNullNullable()
{
int? value = null;
var box = new Box { Element = value };
var copy = SerializerClone(box);
Assert.AreEqual(box.Element, copy.Element);
}
[Test]
public void ShouldSerializeConcurrentDictionary()
{
var dictionary = new ConcurrentDictionary<int, int>();
for(var i = 0; i < 10; i++)
{
dictionary.TryAdd(i, 2 * i);
}
var copy = SerializerClone(dictionary);
CollectionAssert.AreEquivalent(dictionary, copy);
}
[Test]
public void ShouldHandleTwoSerializations()
{
var someObjects = new object[] { "One", 2, null, "Four" };
var stream = new MemoryStream();
var serializer = new Serializer(GetSettings());
serializer.Serialize(someObjects, stream);
serializer.Serialize(someObjects, stream);
stream.Seek(0, SeekOrigin.Begin);
var copy = serializer.Deserialize<object[]>(stream);
CollectionAssert.AreEqual(someObjects, copy);
copy = serializer.Deserialize<object[]>(stream);
CollectionAssert.AreEqual(someObjects, copy);
}
[Test]
public void ShouldDetectStreamShift()
{
var errorneousObject = new BadlySerializable();
try
{
SerializerClone(errorneousObject);
Assert.Fail("Exception was not thrown despite stream corruption.");
}
catch(InvalidOperationException)
{
}
}
[Test]
public void ShouldSerializeTuple()
{
var tuple = Tuple.Create("One", "Two", "Three");
var copy = SerializerClone(tuple);
Assert.AreEqual(tuple, copy);
}
[Test]
public void ShouldInvokeConstructorForFieldInStruct()
{
var withCtor = new StructWithConstructorAttribute();
var copy = SerializerClone(withCtor);
Assert.IsTrue(copy.IsFieldConstructed, "[Constructor] marked field was not initialized.");
}
[Test]
public void ShouldInvokeConstructorForFieldInStructComplex()
{
var withCtor = new StructWithConstructorAttributeOnNestedStruct();
var copy = SerializerClone(withCtor);
Assert.IsTrue(copy.IsFieldConstructed, "[Constructor] marked field was not initialized.");
}
[Test]
public void ShouldInvokeConstructorForField()
{
var withCtor = new WithConstructorAttribute();
var copy = SerializerClone(withCtor);
Assert.IsTrue(copy.IsFieldConstructed, "[Constructor] marked field was not initialized.");
}
[Test]
public void ShouldOmitTransientClass()
{
SerializerClone(new Box { Element = new TransientClass() });
}
[Test]
public void ShouldOmitTransientField()
{
var trans = new ClassWithTransientField() { a = 147, b = 256, c = 850 };
var scd = SerializerClone(trans);
Assert.AreEqual(trans.a, scd.a);
Assert.AreEqual(default(int), scd.b);
Assert.AreEqual(trans.c, scd.c);
}
[Test]
public void ShouldSerializeQueue()
{
var queue = new Queue<int>();
queue.Enqueue(1);
queue.Enqueue(3);
queue.Enqueue(5);
var copy = SerializerClone(queue);
CollectionAssert.AreEqual(queue, copy);
}
[Test]
public void ShouldSerializeByteArray()
{
var array = new byte[] { 2, 4, 6, 8, 10 };
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array, copy);
}
[Test]
public void ShouldSerializeEvent()
{
var withEvent = new ClassWithEvent();
var companion = new CompanionToClassWithEvent();
withEvent.Event += companion.Method;
var pair = Tuple.Create(withEvent, companion);
var copy = SerializerClone(pair);
copy.Item1.Invoke();
Assert.AreEqual(1, copy.Item2.Counter);
}
[Test]
public void ShouldSerializeMulticastEvent()
{
var withEvent = new ClassWithEvent();
var companion1 = new CompanionToClassWithEvent();
withEvent.Event += companion1.Method;
var companion2 = new CompanionToClassWithEvent();
withEvent.Event += companion2.Method;
var triple = Tuple.Create(withEvent, companion1, companion2);
var copy = SerializerClone(triple);
copy.Item1.Invoke();
Assert.AreEqual(1, copy.Item2.Counter);
Assert.AreEqual(1, copy.Item3.Counter);
}
[Test]
public void ShouldSerializeDelegateWithTargetFromDifferentModule()
{
var withEvent = new ClassWithEvent();
var companion = new CompanionSecondModule();
withEvent.Event += companion.MethodAsExtension;
var pair = Tuple.Create(withEvent, companion);
var copy = SerializerClone(pair);
copy.Item1.Invoke();
Assert.AreEqual(1, copy.Item2.Counter);
}
[Test]
public void ShouldSerializeDelegateWithInterfaceMethod()
{
var withEvent = new ClassWithEvent<IInterfaceForDelegate>();
var companion = new ClassImplementingInterfaceForDelegate();
withEvent.Event += companion.Method;
var pair = Tuple.Create(withEvent, companion);
var copy = SerializerClone(pair);
copy.Item1.Invoke(null);
Assert.AreEqual(true, copy.Item2.Invoked);
}
[Test]
public void ShouldSerializeDelegateWithLambdaAttached()
{
var withEvent = new ClassWithEvent();
var companion = new ClassImplementingInterfaceForDelegate();
withEvent.Event += () => companion.Method(null);
var pair = Tuple.Create(withEvent, companion);
var copy = SerializerClone(pair);
copy.Item1.Invoke();
Assert.AreEqual(true, copy.Item2.Invoked);
}
[Test]
public void ShouldSerializeEmptyArray()
{
var emptyArray = new int[0];
var copy = SerializerClone(emptyArray);
CollectionAssert.AreEqual(emptyArray, copy);
}
[Test]
public void ShouldSerializeEmptyList()
{
var emptyList = new List<int>();
var copy = SerializerClone(emptyList);
CollectionAssert.AreEqual(emptyList, copy);
}
[Test]
public void ShouldSerializeByteEnum()
{
var byteEnum = EnumByte.Two;
var copy = SerializerClone(byteEnum);
Assert.AreEqual(byteEnum, copy);
}
[Test]
public void ShouldThrowOnThreadLocalSerialization()
{
var classWithThreadLocal = new ClassWithThreadLocal();
Assert.Throws(typeof(InvalidOperationException), () => SerializerClone(classWithThreadLocal));
}
[Test]
public void ShouldOmitDelegateWithTransientTarget()
{
var withEvent = new ClassWithEvent();
var target1 = new TransientClassWithMethod();
var target2 = new CompanionToClassWithEvent();
withEvent.Event += target2.Method;
withEvent.Event += target1.Method;
var copy = SerializerClone(withEvent);
copy.Invoke();
}
[Test]
public void ShouldSerializeEmptyDelegateList()
{
var withEvent = new ClassWithEvent();
var target = new TransientClassWithMethod();
withEvent.Event += target.Method;
SerializerClone(withEvent);
}
[Test]
public void TransientDerivedShouldAlsoBeTransient()
{
var transientDerived = new TransientDerived();
var values = new object[] { 0, transientDerived };
var copy = SerializerClone(values);
CollectionAssert.AreEqual(new object[] { 0, null }, copy);
}
[Test]
public void ShouldSerializeNongenericStack()
{
var stack = new Stack();
var test = "It works!";
foreach(var chr in test)
{
stack.Push(chr);
}
var copy = SerializerClone(stack);
CollectionAssert.AreEqual(stack, copy);
}
[Test]
public void ShouldSerializeGenericStack()
{
var stack = new Stack<char>();
var test = "It works!";
foreach(var chr in test)
{
stack.Push(chr);
}
var copy = SerializerClone(stack);
CollectionAssert.AreEqual(stack, copy);
}
[Test]
public void ShouldSerializeBoxWithGuid()
{
var box = new GenericBox<Guid>();
box.Element = Guid.NewGuid();
var copy = SerializerClone(box);
Assert.AreEqual(box.Element, copy.Element);
}
[Test]
public void ShouldSerializeClassWithGuid()
{
var withGuid = new ClassWithGuid();
var copy = SerializerClone(withGuid);
Assert.AreEqual(withGuid, copy);
}
[Test]
public void ShouldFindPathToPointerContainingObject()
{
Exception exception = null;
var toClone = new GenericBox<AnotherContainingType>();
toClone.Element = new AnotherContainingType();
try
{
SerializerClone(toClone);
Assert.Fail("Exception was not thrown.");
}
catch(Exception e)
{
exception = e;
}
if(!useGeneratedSerialization)
{
Assert.IsTrue(exception.Message.Contains(toClone.GetType().Name));
Assert.IsTrue(exception.Message.Contains(toClone.Element.GetType().Name));
Assert.IsTrue(exception.Message.Contains(toClone.Element.WithIntPtr.GetType().Name));
}
}
[Test]
public void GithubIssue60()
{
object instance = new Issue60OuterClass() {
Field1 = 0,
Inner = new Issue60InnerClass() {
Field2 = 1,
Field3 = 2,
}
};
SerializerClone(instance);
}
[Test]
public void ShouldSerializeKeyValuePair()
{
var src = new KeyValuePair<int, int>(12, 34);
var dst = SerializerClone(src);
Assert.AreEqual(src.Key, dst.Key);
Assert.AreEqual(src.Value, dst.Value);
}
[Test]
public void ShouldSerializeNullableEnum()
{
var src = new ClassWithNullableEnum() { e = SimpleEnum.Two };
var dst = SerializerClone(src);
Assert.AreEqual(SimpleEnum.Two, dst.e);
}
[Test]
public void ShouldSerializeGenericClassWithLatePostDeserializationHook()
{
var src = new GenericClassWithLatePostDeserializationHook { x = 150 };
var dst = SerializerClone(src);
Assert.AreEqual(147, dst.x);
}
[Test]
public void ShouldSerialize2DArrayWithAllDimensionZero()
{
var array = new int[0, 0];
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array, copy);
}
[Test]
public void ShouldSerialize3DArrayWithOneDimensionZero()
{
var array = new int[2, 0, 2];
var copy = SerializerClone(array);
CollectionAssert.AreEqual(array, copy);
}
[Test]
public void ShouldNotSerializePointer()
{
if(Environment.OSVersion.Platform == PlatformID.Unix && Environment.OSVersion.Version < Version.Parse("4.0"))
{
Assert.Inconclusive("Due to a bug in mono implementation this test is ignored on Linux");
}
var classWithPtr = new ClassWithPointer();
try
{
SerializerClone(classWithPtr);
Assert.Fail("A class with pointer was serialized, but it should have been not.");
}
catch(InvalidOperationException)
{
}
}
[Test]
public void ShouldNotSerializeNull()
{
try
{
SerializerClone<object>(null);
Assert.Fail("Serialization of null succeeded while it should not.");
}
catch(ArgumentException)
{
}
}
[Test]
public void ShouldNotSerializeTransient()
{
try
{
SerializerClone(new TransientClass());
Assert.Fail("Serialization of a transient object succeeded while it should not.");
}
catch(ArgumentException)
{
}
}
[Test]
public void ShouldSerializeTwoTimesDifferentObjects()
{
var serializer = new Serializer(GetSettings());
using(var stream = new MemoryStream())
{
var obj = new SimpleContainer();
obj.First = new SimpleClass();
obj.Second = new SimpleClass();
serializer.Serialize(obj, stream);
stream.Seek(0, SeekOrigin.Begin);
var copy = serializer.Deserialize<SimpleContainer>(stream);
Assert.IsNotNull(copy);
}
using(var stream = new MemoryStream())
{
var obj = new object[] { new SimpleClass(), new SimpleContainer(), new SimpleContainer() };
serializer.Serialize(obj, stream);
stream.Seek(0, SeekOrigin.Begin);
var copy = serializer.Deserialize<object[]>(stream);
Assert.IsNotNull(copy);
Assert.IsInstanceOf<SimpleClass>(copy[0]);
Assert.IsInstanceOf<SimpleContainer>(copy[1]);
Assert.IsInstanceOf<SimpleContainer>(copy[2]);
}
}
[Test]
public void ShouldSerializeClassWithGenericMethodDelegate()
{
var serializer = new Serializer(GetSettings());
var obj = new GenericClassWithGenericDelegate<List<bool>>();
using(var stream = new MemoryStream())
{
serializer.Serialize(obj, stream);
stream.Seek(0, SeekOrigin.Begin);
var copy = serializer.Deserialize<GenericClassWithGenericDelegate<List<bool>>>(stream);
Assert.IsNotNull(copy);
}
}
[Test]
public void ShouldSerializeClassWithArrayOfNestedGenericArguments()
{
var serializer = new Serializer(GetSettings());
var obj = new GenericClassWithNestedGenericArgument<string>("test");
using(var stream = new MemoryStream())
{
serializer.Serialize(obj, stream);
stream.Seek(0, SeekOrigin.Begin);
var copy = serializer.Deserialize<GenericClassWithNestedGenericArgument<string>>(stream);
Assert.IsNotNull(copy);
Assert.AreEqual("test", copy.field[0].field);
}
}
[Test]
public void ShouldSerializeDeepGraph()
{
var obj = new ClassWithReferenceToTheObjectOfTheSameType();
var current = obj;
for(int i = 0; i < 45000; i++)
{
current.reference = new ClassWithReferenceToTheObjectOfTheSameType();
current = current.reference;
}
var serializer = new Serializer(GetSettings());
ClassWithReferenceToTheObjectOfTheSameType copy;
using(var stream = new MemoryStream())
{
serializer.Serialize(obj, stream);
stream.Seek(0, SeekOrigin.Begin);
copy = serializer.Deserialize<ClassWithReferenceToTheObjectOfTheSameType>(stream);
}
Assert.IsNotNull(copy);
var depth = 0;
current = copy;
while(current.reference != null)
{
depth++;
current = current.reference;
}
Assert.AreEqual(45000, depth);
}
[Test]
public void ShouldSerializeListOfInterfaces()
{
var serializer = new Serializer(GetSettings());
var obj = new ClassWithTheListOfInterfaces();
obj.list = new List<IInterface>();
for(int i = 0; i < 100; i++)
{
obj.list.Add(i % 2 == 0 ? (IInterface)new ImplementingInterfaceOne() : (IInterface)new ImplementingInterfaceTwo());
}
using(var stream = new MemoryStream())
{
serializer.Serialize(obj, stream);
stream.Seek(0, SeekOrigin.Begin);
var copy = serializer.Deserialize<ClassWithTheListOfInterfaces>(stream);
Assert.IsNotNull(copy);
Assert.AreEqual(100, copy.list.Count);
}
}
[Test]
public void ShouldHandleEventWithBackreference()
{
var machine = new BoxingClass();
var copy = SerializerClone(machine);
Assert.IsNotNull(copy);
for(var i = 0; i < copy.objectWithItems.items.Length; i++)
{
Assert.IsFalse(copy.objectWithItems.items [i].ActionIsNull, string.Format("Array element: {0}", i));
}
}
[Test]
public void ShouldSerializeDictionaryWithCustomObject()
{
var dic = new Dictionary<CustomObject, string>();
dic.Add(new CustomObject(new object()), "test1");
dic.Add(new CustomObject(new object()), "test2");
dic.Add(new CustomObject(new object()), "test3");
var copy = SerializerClone(dic);
Assert.AreEqual(3, copy.Count);
}
[Test]
public void ShouldSerializeHashsetWithCustomObject()
{
var hset = new HashSet<CustomObject>();
hset.Add(new CustomObject(new object()));
hset.Add(new CustomObject(new object()));
hset.Add(new CustomObject(new object()));
var copy = SerializerClone(hset);
Assert.AreEqual(3, copy.Count);
}
private class CustomObject
{
public CustomObject(object field)
{
this.field = field;
}
public override int GetHashCode()
{
return field.GetHashCode();
}
private object field;
}
private class BoxingClass
{
// it is necessary for fields to be serialized in proper order
// to detect a problem;
// as fileds are ordered alphabetically, please be
// carefull while renaming them
public ClassWithAction objectWithAction;
public ClassWithItems objectWithItems;
public BoxingClass()
{
objectWithItems = new ClassWithItems(this);
}
}
private class ClassWithItems
{
public ClassWithItems(BoxingClass mach)
{
items = new ClassWithAction[2];
for(var i = 0; i < items.Length; i++)
{
items[i] = new ClassWithAction(mach);
// passing 'i' variable to the method is crucial here
items[i].Action += () => Method(i);
}
}
private void Method(int i) { }
public ClassWithAction[] items;
}
private class ClassWithAction
{
public ClassWithAction(BoxingClass box)
{
box.objectWithAction = this;
}
public event Action Action;
public bool ActionIsNull
{
get { return Action == null; }
}
}
public class ClassWithTheListOfInterfaces
{
public List<IInterface> list;
}
public interface IInterface
{
void Method();
}
public class ImplementingInterfaceOne : IInterface
{
public void Method() {}
}
public class ImplementingInterfaceTwo : IInterface
{
public void Method() {}
}
public class ClassWithReferenceToTheObjectOfTheSameType
{
public ClassWithReferenceToTheObjectOfTheSameType reference;
}
public class GenericClassWithNestedGenericArgument<T>
{
public GenericClassWithNestedGenericArgument(T t)
{
field = new [] { new GenericClass<T>() } ;
field[0].field = t;
}
public GenericClass<T>[] field;
}
public class GenericClass<T>
{
public T field;
}
public class GenericClassWithGenericDelegate<T>
{
public GenericClassWithGenericDelegate()
{
del = Action;
}
public void CallDelegate(T c)
{
del(c, "string");
}
private void Action<TInner>(TInner x, string y)
{
}
private readonly Action<T, string> del;
}
public class GenericClassWithTransient<T>
{
[Transient]
public T x;
}
public class GenericClassWithLatePostDeserializationHook : GenericClassWithTransient<int>
{
[LatePostDeserialization]
public void LatePostDeserialization()
{
if(x == 0)
{
x = 147;
}
}
}
public class ClassWithNullableEnum
{
public SimpleEnum? e { get; set; }
}
public class SimpleClass
{
public int Value { get; set; }
public string Str { get; set; }
public override bool Equals(object obj)
{
if(obj == null)
{
return false;
}
if(ReferenceEquals(this, obj))
{
return true;
}
if(obj.GetType() != typeof(SimpleClass))
{
return false;
}
var other = (SimpleClass)obj;
return Value == other.Value && Str == other.Str;
}
public override int GetHashCode()
{
unchecked
{
return Value.GetHashCode() ^ 33 * (Str != null ? Str.GetHashCode() : 0);
}
}
public override string ToString()
{
return string.Format("[SimpleClass: Value={0}, Str={1}]", Value, Str);
}
}
public class SimpleContainer
{
public SimpleClass First { get; set; }
public SimpleClass Second { get; set; }
}
public class CyclicRef
{
public CyclicRef Another { get; set; }
public int Val { get; set; }
}
public class ClassWithReadonly
{
public ClassWithReadonly(int readonlyValue)
{
privateReadonlyField = readonlyValue;
}
public int ReadonlyValue
{
get
{
return privateReadonlyField;
}
}
private readonly int privateReadonlyField;
}
public class ClassWithIntPtr
{
public IntPtr Ptr { get; set; }
}
public class ClassWithSpinLock
{
public SpinLock SLock { get; set; }
}
public class SimpleBaseClass
{
public int BaseField { get; set; }
}
public class SimpleDerivedClass : SimpleBaseClass
{
public int DerivedField { get; set; }
}
public class Box
{
public object Element { get; set; }
}
public sealed class GenericBox<T>
{
public T Element { get; set; }
public override bool Equals(object obj)
{
if(obj == null)
{
return false;
}
if(ReferenceEquals(this, obj))
{
return true;
}
if(obj.GetType() != typeof(GenericBox<T>))
{
return false;
}
var other = (GenericBox<T>)obj;
if(Element != null)
{
return Element.Equals(other.Element);
}
return other.Element == null;
}
public override int GetHashCode()
{
unchecked
{
return (Element != null ? Element.GetHashCode() : 0);
}
}
}
public struct StructGenericBox<T>
{
public T Element;
}
public enum SimpleEnum
{
One,
Two,
Three
}
private enum EnumLong : long
{
First = -1,
Second = long.MaxValue - 12345,
Third
}
private enum EnumShort : short
{
First = short.MinValue + 2,
Second = 6,
Third
}
private enum EnumByte : byte
{
One = 1,
Two = 200
}
[Flags]
private enum TestEnumFlags
{
First = 1,
Second = 2,
Third = 4
}
private struct SimplerStruct
{
public int A;
public long B;
public override string ToString()
{
return string.Format("[SimplerStruct: A={0}, B={1}]", A, B);
}
}
private struct SimpleStruct
{
public int A;
public String B;
public override string ToString()
{
return string.Format("[SimpleStruct: A={0}, B={1}]", A, B);
}
}
private struct ComplexStruct
{
public int A;
public SimpleStruct B;
public override string ToString()
{
return string.Format("[ComplexStruct: A={0}, B={1}]", A, B);
}
}
private class SpeciallySerializable : ISpeciallySerializable
{
public void Fill(int length)
{
var bytes = new byte[length];
Helpers.Random.NextBytes(bytes);
data = bytes.Select(x => new IntPtr(x)).ToArray();
}
public byte[] Data
{
get
{
return data.Select(x => (byte)x).ToArray();
}
}
public void Load(PrimitiveReader reader)
{
var length = reader.ReadInt32();
data = new IntPtr[length];
for(var i = 0; i < length; i++)
{
data[i] = new IntPtr(reader.ReadByte());
}
}
public void Save(PrimitiveWriter writer)
{
writer.Write(data.Length);
for(var i = 0; i < data.Length; i++)
{
writer.Write((byte)data[i]);
}
}
private IntPtr[] data;
}
private class BadlySerializable : ISpeciallySerializable
{
public void Load(PrimitiveReader reader)
{
reader.ReadInt32();
reader.ReadInt32();
}
public void Save(PrimitiveWriter writer)
{
for(var i = 0; i < 3; i++)
{
writer.Write(666 + i);
}
}
}
private struct StructWithConstructorAttribute
{
public bool IsFieldConstructed
{
get
{
return resetEvent != null;
}
}
#pragma warning disable 649
[Constructor(false)]
private ManualResetEvent resetEvent;
#pragma warning restore 649
}
private struct StructWithConstructorAttributeOnNestedStruct
{
public bool IsFieldConstructed
{
get
{
return s.field == 5;
}
}
#pragma warning disable 649
[ConstructorAttribute(true)]
private NestedStruct s;
#pragma warning restore 649
}
private struct NestedStruct
{
public NestedStruct(bool condition)
{
field = (condition ? 5 : 0);
}
#pragma warning disable 649
public int field;
#pragma warning restore 649
}
private class WithConstructorAttribute
{
public bool IsFieldConstructed
{
get
{
return resetEvent != null;
}
}
#pragma warning disable 649
[Constructor(false)]
private ManualResetEvent resetEvent;
#pragma warning restore 649
}
[Transient]
private class TransientClass
{
public IntPtr Pointer { get; set; }
}
private class TransientDerived : TransientClass
{
public int Integer { get; set; }
}
private class ClassWithTransientField
{
public int a;
[Transient]
public int
b;
public int c;
}
private class ClassWithEvent
{
public event Action Event;
public void Invoke()
{
var toInvoke = Event;
if(toInvoke != null)
{
toInvoke();
}
}
}
private class CompanionToClassWithEvent
{
public int Counter { get; set; }
public void Method()
{
Counter++;
}
}
private class ClassWithThreadLocal
{
public ClassWithThreadLocal()
{
ThreadLocal = new ThreadLocal<int>();
}
public ThreadLocal<int> ThreadLocal { get; set; }
}
[Transient]
private class TransientClassWithMethod
{
public TransientClassWithMethod()
{
a = 1;
}
public void Method()
{
a++;
}
private int a;
}
public class Issue60OuterClass
{
public decimal Field1;
public Issue60InnerClass Inner { get; set; }
}
public class Issue60InnerClass
{
public decimal? Field2;
public decimal? Field3;
}
public class ClassWithGuid
{
public ClassWithGuid()
{
Id = Guid.NewGuid();
Number = Id.ToByteArray()[1] * Id.ToByteArray()[0];
Str = Helpers.GetRandomString(Number / 256);
}
public override bool Equals(object obj)
{
if(obj == null)
{
return false;
}
if(ReferenceEquals(this, obj))
{
return true;
}
if(obj.GetType() != typeof(ClassWithGuid))
{
return false;
}
ClassWithGuid other = (ClassWithGuid)obj;
return Id == other.Id && Number == other.Number && Str == other.Str;
}
public override int GetHashCode()
{
unchecked
{
return Id.GetHashCode() ^ Number.GetHashCode() ^ (Str != null ? Str.GetHashCode() : 0);
}
}
public override string ToString()
{
return string.Format("[ClassWithGuid: Id={0}, Number={1}, Str={2}]", Id, Number, Str);
}
public Guid Id { get; private set; }
public int Number { get; private set; }
public string Str { get; private set; }
}
public class AnotherContainingType
{
public AnotherContainingType()
{
WithIntPtr = new ClassWithIntPtr();
}
public ClassWithIntPtr WithIntPtr { get; private set; }
}
public unsafe class ClassWithPointer
{
protected int* Pointer;
}
private class ClassWithEvent<T>
{
public event Action<T> Event;
public void Invoke(T arg)
{
var toInvoke = Event;
if(toInvoke != null)
{
toInvoke(arg);
}
}
}
private interface IInterfaceForDelegate
{
void Method(IInterfaceForDelegate arg);
}
private class ClassImplementingInterfaceForDelegate : IInterfaceForDelegate
{
public bool Invoked { get; private set; }
public void Method(IInterfaceForDelegate arg)
{
Invoked = true;
}
}
private readonly bool useGeneratedSerialization;
}
public static class CompanionExtensions
{
public static void MethodAsExtension(this CompanionSecondModule companion)
{
companion.Counter++;
}
}
}