// // 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 NUnit.Framework; using System.IO; using Antmicro.Migrant; using System.Collections.Generic; using System.Reflection; namespace Antmicro.Migrant.Tests { [TestFixture(false, false, true)] [TestFixture(true, false, true)] [TestFixture(false, true, true)] [TestFixture(true, true, true)] [TestFixture(false, false, false)] [TestFixture(true, false, false)] [TestFixture(false, true, false)] [TestFixture(true, true, false)] public class SurrogateTests : BaseTestWithSettings { public SurrogateTests(bool useGeneratedSerializer, bool useGeneratedDeserializer, bool useTypeStamping) : base(useGeneratedSerializer, useGeneratedDeserializer, false, false, false, useTypeStamping, true) { } [Test] public void ShouldPlaceObjectForSurrogate() { var b = new SurrogateMockB(); var pseudocopy = PseudoClone(b, serializer => { serializer.ForSurrogate().SetObject(x => new SurrogateMockA(999)); }); var a = pseudocopy as SurrogateMockA; Assert.IsNotNull(a); Assert.AreEqual(999, a.Field); } [Test] public void ShouldPlaceObjectForSurrogatePreservingIdentity() { var b = new SurrogateMockB(); var list = new List { b, new List { b }, new SurrogateMockB() }; var counter = 0; var pseudocopy = PseudoClone(list, serializer => { serializer.ForSurrogate().SetObject(x => new SurrogateMockA(counter++)); }); list = pseudocopy as List; Assert.IsNotNull(list); var sublist = list[1] as List; Assert.IsNotNull(sublist); Assert.AreSame(list[0], sublist[0]); Assert.AreNotSame(list[0], list[2]); var a = list[0] as SurrogateMockA; Assert.IsNotNull(a); Assert.AreEqual(3, a.Field); var secondA = list[2] as SurrogateMockA; Assert.IsNotNull(secondA); Assert.AreEqual(2, secondA.Field); } [Test] public void ShouldPlaceSurrogateForObject() { var b = new SurrogateMockB(); var pseudocopy = PseudoClone(b, serializer => { serializer.ForObject().SetSurrogate(x => new SurrogateMockA(1)); }); var a = pseudocopy as SurrogateMockA; Assert.IsNotNull(a); Assert.AreEqual(1, a.Field); } [Test] public void ShouldPlaceSurrogateForObjectPreservingIdentity() { var b = new SurrogateMockB(); var counter = 0; var list = new List { b, new SurrogateMockB(), b }; var pseudocopy = PseudoClone(list, serializer => { serializer.ForObject().SetSurrogate(x => new SurrogateMockA(counter++)); }); list = pseudocopy as List; Assert.IsNotNull(list); Assert.AreSame(list[0], list[2]); Assert.AreNotSame(list[0], list[1]); var a = list[0] as SurrogateMockA; Assert.IsNotNull(a); Assert.AreEqual(counter - 2, a.Field); var secondA = list[1] as SurrogateMockA; Assert.IsNotNull(secondA); Assert.AreEqual(counter - 1, secondA.Field); } [Test] public void ShouldAllowBoxedValueTypeSurrogation() { int a = 199; var pseudocopy = PseudoClone(a, serializer => { serializer.ForObject().SetSurrogate(x => new IntSurrogate(x)); serializer.ForSurrogate().SetObject(x => (object)(x.cwi.field)); }); var b = pseudocopy as object; Assert.AreEqual(299, b); } private class ClassWithInteger { public int field; } private class IntSurrogate { public IntSurrogate(int value) { cwi = new ClassWithInteger { field = value + 100 }; } public ClassWithInteger cwi; } [Test] public void ShouldDoSurrogateObjectSwap() { var b = new SurrogateMockB(); var pseudocopy = PseudoClone(b, serializer => { serializer.ForObject().SetSurrogate(x => new SurrogateMockA(1)); serializer.ForSurrogate().SetObject(x => new SurrogateMockC()); }); var c = pseudocopy as SurrogateMockC; Assert.IsNotNull(c); } [Test] public void ShouldPlaceObjectForDerivedSurrogate() { var d = new SurrogateMockD(); var pseudocopy = PseudoClone(d, serializer => { serializer.ForSurrogate().SetObject(x => new SurrogateMockB()); }); var b = pseudocopy as SurrogateMockB; Assert.IsNotNull(b); } [Test] public void ShouldPlaceSurrogateForDerivedObject() { var d = new SurrogateMockD(); var pseudocopy = PseudoClone(d, serializer => { serializer.ForObject().SetSurrogate(x => new SurrogateMockB()); }); var b = pseudocopy as SurrogateMockB; Assert.IsNotNull(b); } [Test] public void ShouldPlaceObjectForSurrogateImplementingInterface() { var e = new SurrogateMockE(); var pseudocopy = PseudoClone(e, serializer => { serializer.ForSurrogate().SetObject(x => new SurrogateMockB()); }); var b = pseudocopy as SurrogateMockB; Assert.IsNotNull(b); } [Test] public void ShouldPlaceSurrogateForObjectImplementingInterface() { var e = new SurrogateMockE(); var pseudocopy = PseudoClone(e, serializer => { serializer.ForObject().SetSurrogate(x => new SurrogateMockB()); }); var b = pseudocopy as SurrogateMockB; Assert.IsNotNull(b); } [Test] public void ShouldUseMoreSpecificSurrogateIfPossible() { var mock = new SurrogateMockD(); var pseudocopy = PseudoClone(mock, serializer => { serializer.ForObject().SetSurrogate(x => new SurrogateMockA(1)); serializer.ForObject().SetSurrogate(x => new SurrogateMockB()); }); var b = pseudocopy as SurrogateMockB; Assert.IsNotNull(b); } [Test] public void ShouldThrowWhenSettingSurrogatesAfterSerialization() { var serializer = new Serializer(GetSettings()); serializer.Serialize(new object(), Stream.Null); Assert.Throws(() => serializer.ForObject().SetSurrogate(x => new object())); } [Test] public void ShouldThrowWhenSettingObjectForSurrogateAfterDeserialization() { var serializer = new Serializer(GetSettings()); var stream = new MemoryStream(); serializer.Serialize(new object(), stream); stream.Seek(0, SeekOrigin.Begin); serializer.Deserialize(stream); Assert.Throws(() => serializer.ForSurrogate().SetObject(x => new object())); } [Test] public void ShouldDoSurrogateObjectSwapTwoTimes() { var b = new SurrogateMockB(); var serializer = new Serializer(GetSettings()); serializer.ForObject().SetSurrogate(x => new SurrogateMockA(1)); serializer.ForSurrogate().SetObject(x => new SurrogateMockC()); for(var i = 0; i < 2; i++) { using(var stream = new MemoryStream()) { serializer.Serialize(b, stream); stream.Seek(0, SeekOrigin.Begin); var pseudocopy = serializer.Deserialize(stream); var c = pseudocopy as SurrogateMockC; Assert.IsNotNull(c); } } } [Test] public void ShouldUseSurrogateAddedFirstWhenBothMatch() { var h = new SurrogateMockH(); var pseudocopy = PseudoClone(h, serializer => { serializer.ForObject().SetSurrogate(x => "g"); serializer.ForObject().SetSurrogate(x => "e"); }); Assert.AreEqual("g", pseudocopy); } [Test] public void ShouldSwapGenericSurrogateWithObject() { var f = new SurrogateMockF("test"); var pseudocopy = PseudoClone(f, serializer => serializer.ForObject(typeof(SurrogateMockF<>)).SetSurrogate(x => { var type = x.GetType(); return type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)[0].GetValue(x); })); Assert.IsInstanceOf(pseudocopy); } [Test] public void ShouldSwapWithNonGenericSurrogate() { var f = new SurrogateMockF("test"); var pseudocopy = PseudoClone(f, serializer => { serializer.ForObject(typeof(SurrogateMockF<>)).SetSurrogate(x => "general"); serializer.ForObject>().SetSurrogate(x => "special"); }); Assert.AreEqual("special", pseudocopy); } [Test] public void ShouldUseGenericBaseSurrogateForDerivedClass() { var i = new SurrogateMockI("something"); var pseudocopy = PseudoClone(i, serializer => serializer.ForObject(typeof(SurrogateMockF<>)).SetSurrogate(x => "success")); Assert.AreEqual("success", pseudocopy); } [Test] public void ShouldUseMoreSpecificGenericSurrogateIfPossible() { var i = new SurrogateMockI("something"); var pseudocopy = PseudoClone(i, serializer => { serializer.ForObject(typeof(SurrogateMockF<>)).SetSurrogate(x => "fail"); serializer.ForObject(typeof(SurrogateMockI<>)).SetSurrogate(x => "success"); }); Assert.AreEqual("success", pseudocopy); } [Test] public void ShouldTreatNullAsDontSurrogateThisType() { var d = new SurrogateMockD(); var pseudocopy = PseudoClone(d, serializer => { serializer.ForObject(typeof(SurrogateMockC)).SetSurrogate(x => "fail"); serializer.ForObject(typeof(SurrogateMockD)).SetSurrogate(null); }); Assert.IsInstanceOf(pseudocopy); } [Test] public void ShouldDeserializeSurrogatePointingToItself() { var obj = new object(); var pseudocopy = PseudoClone(obj, serializer => { serializer.ForSurrogate(typeof(object)).SetObject(x => { var j = new SurrogateMockJ(); j.field = j; return j; }); }); Assert.IsInstanceOf(pseudocopy); Assert.AreEqual(pseudocopy, ((SurrogateMockJ)pseudocopy).field); } [Test] public void ShouldNotGenerateGenericSurrogateTwice() { var d = new ClassWithGenericSurrogates(); Serializer serializerInstance = null; int surrogatesBeforeSerialization = 0; var pseudocopy = PseudoClone(d, serializer => { serializer.ForObject(typeof(ClassToBeSurrogated<>)).SetSurrogateGenericType(typeof(SurrogatingClass<>)); serializerInstance = serializer; surrogatesBeforeSerialization = serializer.surrogatesForObjects.Count; }); Assert.IsInstanceOf(pseudocopy); Assert.IsInstanceOf>(((ClassWithGenericSurrogates)pseudocopy).fieldA); Assert.IsInstanceOf>(((ClassWithGenericSurrogates)pseudocopy).fieldB); Assert.IsInstanceOf>(((ClassWithGenericSurrogates)pseudocopy).fieldC); Assert.AreEqual(useGeneratedSerializer ? 2 : 0, serializerInstance.surrogatesForObjects.Count - surrogatesBeforeSerialization); } private class ClassWithGenericSurrogates { internal object fieldA = new ClassToBeSurrogated(); internal object fieldB = new ClassToBeSurrogated(); internal object fieldC = new ClassToBeSurrogated(); } private class ClassToBeSurrogated { } private class SurrogatingClass { public SurrogatingClass(ClassToBeSurrogated x) { } } } public class SurrogateMockA { public SurrogateMockA(int field) { Field = field; } public int Field { get; private set; } } public class SurrogateMockB { } public class SurrogateMockC { } public class SurrogateMockD : SurrogateMockC { } public interface ISurrogateMockE { } public class SurrogateMockE : ISurrogateMockE { } public class SurrogateMockF { public SurrogateMockF(T value) { Value = value; } public T Value { get; private set; } } public interface ISurrogateMockG { } public class SurrogateMockH : ISurrogateMockE, ISurrogateMockG { } public class SurrogateMockI : SurrogateMockF { public SurrogateMockI(T value) : base(value) { } } public class SurrogateMockJ { public SurrogateMockJ field; } }