439 lines
14 KiB
C#
439 lines
14 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 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<SurrogateMockB>().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<object> { b, new List<object> { b }, new SurrogateMockB() };
|
|
var counter = 0;
|
|
var pseudocopy = PseudoClone(list, serializer =>
|
|
{
|
|
serializer.ForSurrogate<SurrogateMockB>().SetObject(x => new SurrogateMockA(counter++));
|
|
});
|
|
list = pseudocopy as List<object>;
|
|
Assert.IsNotNull(list);
|
|
var sublist = list[1] as List<object>;
|
|
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<SurrogateMockB>().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<object> { b, new SurrogateMockB(), b };
|
|
var pseudocopy = PseudoClone(list, serializer =>
|
|
{
|
|
serializer.ForObject<SurrogateMockB>().SetSurrogate(x => new SurrogateMockA(counter++));
|
|
});
|
|
list = pseudocopy as List<object>;
|
|
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<int>().SetSurrogate(x => new IntSurrogate(x));
|
|
serializer.ForSurrogate<IntSurrogate>().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<SurrogateMockB>().SetSurrogate(x => new SurrogateMockA(1));
|
|
serializer.ForSurrogate<SurrogateMockA>().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<SurrogateMockC>().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<SurrogateMockC>().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<ISurrogateMockE>().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<ISurrogateMockE>().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<SurrogateMockC>().SetSurrogate(x => new SurrogateMockA(1));
|
|
serializer.ForObject<SurrogateMockD>().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<InvalidOperationException>(() => serializer.ForObject<object>().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<object>(stream);
|
|
Assert.Throws<InvalidOperationException>(() => serializer.ForSurrogate<object>().SetObject(x => new object()));
|
|
}
|
|
|
|
[Test]
|
|
public void ShouldDoSurrogateObjectSwapTwoTimes()
|
|
{
|
|
var b = new SurrogateMockB();
|
|
var serializer = new Serializer(GetSettings());
|
|
serializer.ForObject<SurrogateMockB>().SetSurrogate(x => new SurrogateMockA(1));
|
|
serializer.ForSurrogate<SurrogateMockA>().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<object>(stream);
|
|
var c = pseudocopy as SurrogateMockC;
|
|
Assert.IsNotNull(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ShouldUseSurrogateAddedFirstWhenBothMatch()
|
|
{
|
|
var h = new SurrogateMockH();
|
|
var pseudocopy = PseudoClone(h, serializer =>
|
|
{
|
|
serializer.ForObject<ISurrogateMockG>().SetSurrogate(x => "g");
|
|
serializer.ForObject<ISurrogateMockE>().SetSurrogate(x => "e");
|
|
});
|
|
Assert.AreEqual("g", pseudocopy);
|
|
}
|
|
|
|
[Test]
|
|
public void ShouldSwapGenericSurrogateWithObject()
|
|
{
|
|
var f = new SurrogateMockF<string>("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<string>(pseudocopy);
|
|
}
|
|
|
|
[Test]
|
|
public void ShouldSwapWithNonGenericSurrogate()
|
|
{
|
|
var f = new SurrogateMockF<string>("test");
|
|
var pseudocopy = PseudoClone(f, serializer =>
|
|
{
|
|
serializer.ForObject(typeof(SurrogateMockF<>)).SetSurrogate(x => "general");
|
|
serializer.ForObject<SurrogateMockF<string>>().SetSurrogate(x => "special");
|
|
});
|
|
Assert.AreEqual("special", pseudocopy);
|
|
}
|
|
|
|
[Test]
|
|
public void ShouldUseGenericBaseSurrogateForDerivedClass()
|
|
{
|
|
var i = new SurrogateMockI<string>("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<string>("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<SurrogateMockD>(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<SurrogateMockJ>(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<ClassWithGenericSurrogates>(pseudocopy);
|
|
Assert.IsInstanceOf<SurrogatingClass<int>>(((ClassWithGenericSurrogates)pseudocopy).fieldA);
|
|
Assert.IsInstanceOf<SurrogatingClass<float>>(((ClassWithGenericSurrogates)pseudocopy).fieldB);
|
|
Assert.IsInstanceOf<SurrogatingClass<int>>(((ClassWithGenericSurrogates)pseudocopy).fieldC);
|
|
Assert.AreEqual(useGeneratedSerializer ? 2 : 0, serializerInstance.surrogatesForObjects.Count - surrogatesBeforeSerialization);
|
|
}
|
|
|
|
private class ClassWithGenericSurrogates
|
|
{
|
|
internal object fieldA = new ClassToBeSurrogated<int>();
|
|
internal object fieldB = new ClassToBeSurrogated<float>();
|
|
internal object fieldC = new ClassToBeSurrogated<int>();
|
|
}
|
|
|
|
private class ClassToBeSurrogated<T>
|
|
{
|
|
}
|
|
|
|
private class SurrogatingClass<T>
|
|
{
|
|
public SurrogatingClass(ClassToBeSurrogated<T> 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<T>
|
|
{
|
|
public SurrogateMockF(T value)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
public T Value { get; private set; }
|
|
}
|
|
|
|
public interface ISurrogateMockG
|
|
{
|
|
|
|
}
|
|
|
|
public class SurrogateMockH : ISurrogateMockE, ISurrogateMockG
|
|
{
|
|
|
|
}
|
|
|
|
public class SurrogateMockI<T> : SurrogateMockF<T>
|
|
{
|
|
public SurrogateMockI(T value) : base(value)
|
|
{
|
|
}
|
|
}
|
|
|
|
public class SurrogateMockJ
|
|
{
|
|
public SurrogateMockJ field;
|
|
}
|
|
}
|
|
|