Commit 18b8d58c authored by Ben Lambell's avatar Ben Lambell

Add support for serializing actions

parent ef9c1d32
/*
* Copyright (c) 2017 Ben Lambell.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.IO;
using NUnit.Framework;
using Recurse.Persist.Serialization.Operations;
namespace Recurse.Persist.Test
{
public static class Deserialize
{
public static T Execute<T>(T @object)
{
var dataStream = new MemoryStream();
var metadataStream = new MemoryStream();
var serialization = RootOperation.CreateWrite(@object, metadataStream, dataStream);
while (serialization.TryContinue()) { }
metadataStream.Position = 0;
dataStream.Position = 0;
var deserialization = RootOperation.CreateRead(metadataStream, dataStream);
while (deserialization.TryContinue()) { }
Assert.AreEqual(metadataStream.Length, metadataStream.Position);
Assert.AreEqual(dataStream.Length, dataStream.Position);
return (T)deserialization.Root;
}
}
}
/*
* Copyright (c) 2017 Ben Lambell.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Runtime.Serialization;
using NUnit.Framework;
using Recurse.Persist.Serializables;
namespace Recurse.Persist.Test
{
public static class SerializableActionTests
{
[Test]
public static void CanSerializeSerializableDelegates()
{
var target = new InstanceClass();
var action = new SerializableAction<int>(target.ReceiveValue);
var result = Deserialize.Execute(action);
// Cannot compare result with target, as serialization and deserialization
// means they actually refer to different objects' method calls
}
[Test]
public static void CanSerializeStaticClassSerializableDelegates()
{
var action = new SerializableAction<int>(StaticClass.StaticReceiveValue);
var result = Deserialize.Execute(action);
Assert.AreEqual(result, action);
}
[Test]
public static void CanSerializeStaticSerializableDelegates()
{
var action = new SerializableAction<int>(InstanceClass.StaticReceiveValue);
var result = Deserialize.Execute(action);
Assert.AreEqual(result, action);
}
private static class StaticClass
{
public static void StaticReceiveValue(int value)
{
}
}
[Serializable]
private class InstanceClass
{
public static void StaticReceiveValue(int value)
{
}
public void ReceiveValue(int value)
{
}
}
}
}
......@@ -8,9 +8,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using NUnit.Framework;
using Recurse.Persist.Serialization.Operations;
namespace Recurse.Persist.Test
{
......@@ -19,72 +17,72 @@ namespace Recurse.Persist.Test
[Test]
public static void CannotSerializeActions()
{
Assert.Throws<InvalidOperationException>(() => Deserialize<Action>(() => 1.ToString()));
Assert.Throws<InvalidOperationException>(() => Deserialize.Execute<Action>(() => 1.ToString()));
}
[Test]
public static void CannotSerializeFunctions()
{
Assert.Throws<InvalidOperationException>(() => Deserialize<Func<int>>(() => 1));
Assert.Throws<InvalidOperationException>(() => Deserialize.Execute<Func<int>>(() => 1));
}
[Test]
public static void CanSerializeIntegers()
{
Assert.AreEqual(0, Deserialize(0));
Assert.AreEqual(1, Deserialize(1));
Assert.AreEqual(69, Deserialize(69));
Assert.AreEqual(-1, Deserialize(-1));
Assert.AreEqual(0, Deserialize.Execute(0));
Assert.AreEqual(1, Deserialize.Execute(1));
Assert.AreEqual(69, Deserialize.Execute(69));
Assert.AreEqual(-1, Deserialize.Execute(-1));
}
[Test]
public static void CanSerializeEmptyArrays()
{
Assert.AreEqual(0, Deserialize(new int[0]).Length);
Assert.AreEqual(0, Deserialize(new object[0]).Length);
Assert.AreEqual(0, Deserialize.Execute(new int[0]).Length);
Assert.AreEqual(0, Deserialize.Execute(new object[0]).Length);
}
[Test]
public static void CanSerializeOneDimensionalArrays()
{
Assert.AreEqual(55, Deserialize(new[] { 44, 55 })[1]);
Assert.AreEqual(55, Deserialize.Execute(new[] { 44, 55 })[1]);
}
[Test]
public static void CanSerializeArrayOfArrays()
{
Assert.AreEqual(44, Deserialize(new[] { new[] { 44 } })[0][0]);
Assert.AreEqual(44, Deserialize.Execute(new[] { new[] { 44 } })[0][0]);
}
[Test]
public static void CanSerializeArrayOfObjects()
{
Assert.IsNotNull(Deserialize(new[] { new object() })[0]);
Assert.IsNotNull(Deserialize.Execute(new[] { new object() })[0]);
}
[Test]
public static void CanSerializeArrayOfCustomObjects()
{
Assert.IsNotNull(Deserialize(new[] { new CustomClass("Hey") })[0]);
Assert.IsNotNull(Deserialize.Execute(new[] { new CustomClass("Hey") })[0]);
}
[Test]
public static void CanSerializeTwoDimensionalArrays()
{
var array = new int[,] { { 44, 55 }, { 44, 88 } };
Assert.AreEqual(88, Deserialize(array)[1,1]);
Assert.AreEqual(88, Deserialize.Execute(array)[1,1]);
}
[Test]
public static void CanSerializeObjects()
{
Assert.IsNotNull(Deserialize(new object()));
Assert.IsNotNull(Deserialize.Execute(new object()));
}
[Test]
public static void CanSerializeNulls()
{
Assert.IsNull(Deserialize<object>(null));
Assert.IsNull(Deserialize.Execute<object>(null));
}
[Test]
......@@ -94,7 +92,7 @@ namespace Recurse.Persist.Test
var r1 = new WeakReference(o1);
var r2 = new WeakReference(new object());
var result = Deserialize(new[] { o1, r1, r2 });
var result = Deserialize.Execute(new[] { o1, r1, r2 });
GC.Collect();
......@@ -119,12 +117,12 @@ namespace Recurse.Persist.Test
[Test]
public static void CanSerializeLists()
{
var result = Deserialize(new List<int> { 0, 1, 2 });
var result = Deserialize.Execute(new List<int> { 0, 1, 2 });
Assert.AreEqual(0, result[0]);
Assert.AreEqual(1, result[1]);
Assert.AreEqual(2, result[2]);
var result2 = Deserialize(new List<int> { 1, 2, 3 });
var result2 = Deserialize.Execute(new List<int> { 1, 2, 3 });
Assert.AreEqual(1, result2[0]);
Assert.AreEqual(2, result2[1]);
Assert.AreEqual(3, result2[2]);
......@@ -133,7 +131,7 @@ namespace Recurse.Persist.Test
[Test]
public static void CanSerializeEmptyLists()
{
var result = Deserialize(new List<int>());
var result = Deserialize.Execute(new List<int>());
Assert.AreEqual(0, result.Count);
}
......@@ -141,7 +139,7 @@ namespace Recurse.Persist.Test
public static void CanSerializeCustomClass()
{
var o = new CustomClass("Hi!") { String = "Hey" };
var result = Deserialize(o);
var result = Deserialize.Execute(o);
Assert.AreEqual(o.String, result.String);
Assert.AreEqual(o.ReadOnlyProperty, result.ReadOnlyProperty);
......@@ -152,7 +150,7 @@ namespace Recurse.Persist.Test
public static void ClassAndBaseClassMembersAreSerialized()
{
var o = new CustomSubClass("Hi!", "Yay!");
var result = Deserialize(o);
var result = Deserialize.Execute(o);
Assert.AreEqual("Hi!", result.ReadOnlyProperty);
Assert.AreEqual("Yay!", ((CustomClass)result).ReadOnlyProperty);
......@@ -168,7 +166,7 @@ namespace Recurse.Persist.Test
b.ObjectProperty = c;
c.ObjectProperty = a;
var result = Deserialize(a);
var result = Deserialize.Execute(a);
Assert.AreEqual(result, ((CustomClass)((CustomClass)result.ObjectProperty).ObjectProperty).ObjectProperty);
}
......@@ -178,107 +176,30 @@ namespace Recurse.Persist.Test
{
var obj = new object();
var refs = new[] { obj, obj };
var result = Deserialize(refs);
var result = Deserialize.Execute(refs);
Assert.IsTrue(result[0] == result[1]);
}
[Test]
public static void CanSerializeNullableMembers()
{
var a = new ClassWithValue<int?>(null);
var b = new ClassWithValue<int?>(999);
var result = Deserialize(new[] { a, b });
Assert.AreEqual(null, result[0].Value);
Assert.AreEqual(999, result[1].Value);
}
[Test]
public static void CanSerializeCustomEnums()
{
var a = new ClassWithValue<CustomEnum>(CustomEnum.Value1);
var b = new ClassWithValue<CustomEnum>(CustomEnum.Value2);
var result = Deserialize(new[] { a, b });
Assert.AreEqual(CustomEnum.Value1, result[0].Value);
Assert.AreEqual(CustomEnum.Value1, result[0].Value);
}
[Test]
public static void CanSerializeCustomStructs()
{
var result = Deserialize(new CustomStruct(33));
Assert.AreEqual(33, result.Value);
}
[Test]
public static void CanSerializeListsOfCustomObjects()
{
var result = Deserialize(new List<CustomClass> { new CustomClass("Um"), new CustomClass("Ah") });
var result = Deserialize.Execute(new List<CustomClass> { new CustomClass("Um"), new CustomClass("Ah") });
Assert.AreEqual("Um", result[0].ReadOnlyProperty);
Assert.AreEqual("Ah", result[1].ReadOnlyProperty);
}
[Test]
public static void CanSerializeListsOfCustomStructs()
{
var list = new List<CustomStruct> { new CustomStruct(33), new CustomStruct(77) };
var result = Deserialize(list);
Assert.AreEqual(33, result[0].Value);
Assert.AreEqual(77, result[1].Value);
}
[Test]
public static void DoesNotSerializeNotSerializedMembers()
{
var obj = new ClassWithNotSerializedValue { Value = 55 };
var result = Deserialize(obj);
var result = Deserialize.Execute(obj);
Assert.AreEqual(0, result.Value);
Assert.AreNotEqual(obj.Value, result.Value);
}
private static T Deserialize<T>(T @object)
{
var dataStream = new MemoryStream();
var metadataStream = new MemoryStream();
var serialization = RootOperation.CreateWrite(@object, metadataStream, dataStream);
while (serialization.TryContinue()) { }
metadataStream.Position = 0;
dataStream.Position = 0;
var deserialization = RootOperation.CreateRead(metadataStream, dataStream);
while (deserialization.TryContinue()) { }
Assert.AreEqual(metadataStream.Length, metadataStream.Position);
Assert.AreEqual(dataStream.Length, dataStream.Position);
return (T)deserialization.Root;
}
private struct CustomStruct
{
public int Value { get; }
public CustomStruct(int value) : this()
{
Value = value;
}
}
private enum CustomEnum
{
Value1, Value2
}
[Serializable]
private class ClassWithNotSerializedValue
{
......@@ -286,17 +207,6 @@ namespace Recurse.Persist.Test
public int Value;
}
[Serializable]
private class ClassWithValue<T>
{
public T Value { get; }
public ClassWithValue(T value)
{
Value = value;
}
}
[Serializable]
private class CustomClass
{
......
/*
* Copyright (c) 2017 Ben Lambell.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using NUnit.Framework;
namespace Recurse.Persist.Test
{
public static class ValueTypeSerializationTests
{
[Test]
public static void CanSerializeNullableMembers()
{
var a = new ClassWithValue<int?>(null);
var b = new ClassWithValue<int?>(999);
var result = Deserialize.Execute(new[] { a, b });
Assert.AreEqual(null, result[0].Value);
Assert.AreEqual(999, result[1].Value);
}
[Test]
public static void CanSerializeCustomEnums()
{
var a = new ClassWithValue<CustomEnum>(CustomEnum.Value1);
var b = new ClassWithValue<CustomEnum>(CustomEnum.Value2);
var result = Deserialize.Execute(new[] { a, b });
Assert.AreEqual(CustomEnum.Value1, result[0].Value);
Assert.AreEqual(CustomEnum.Value1, result[0].Value);
}
[Test]
public static void CanSerializeCustomStructs()
{
var result = Deserialize.Execute(new CustomStruct(33));
Assert.AreEqual(33, result.Value);
}
[Test]
public static void CanSerializeListsOfCustomStructs()
{
var list = new List<CustomStruct> { new CustomStruct(33), new CustomStruct(77) };
var result = Deserialize.Execute(list);
Assert.AreEqual(33, result[0].Value);
Assert.AreEqual(77, result[1].Value);
}
private struct CustomStruct
{
public int Value { get; }
public CustomStruct(int value) : this()
{
Value = value;
}
}
[Serializable]
private class ClassWithValue<T>
{
public T Value { get; }
public ClassWithValue(T value)
{
Value = value;
}
}
private enum CustomEnum
{
Value1, Value2
}
}
}
using System;
using System.Runtime.Serialization;
namespace Recurse.Persist.Serializables
{
[Serializable]
public class SerializableAction : SerializableDelegate<Action>
{
new protected Action Delegate => (Action)base.Delegate;
public SerializableAction(Action action) : base(action)
{
}
public SerializableAction(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
protected override Type[] ArgumentTypes => new Type[0];
}
[Serializable]
public class SerializableAction<T0> : SerializableDelegate<Action<T0>>
{
new protected Action<T0> Delegate => (Action<T0>)base.Delegate;
public SerializableAction(Action<T0> action) : base(action)
{
}
public SerializableAction(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
protected override Type[] ArgumentTypes => new[] { typeof(T0) };
}
[Serializable]
public class SerializableAction<T0, T1> : SerializableDelegate<Action<T0, T1>>
{
new protected Action<T0, T1> Delegate => (Action<T0, T1>)base.Delegate;
public SerializableAction(Action<T0, T1> action) : base(action)
{
}
public SerializableAction(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
protected override Type[] ArgumentTypes => new[] { typeof(T0), typeof(T1) };
}
}
using Recurse.Common.Utils;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Recurse.Persist.Serializables
{
public abstract class SerializableDelegate<TDelegate> : Equatable, ISerializable
{
private object Target => Delegate.Target;
private Type ReflectedType => Delegate.Method.ReflectedType;
private string Name => Delegate.Method.Name;
protected Delegate Delegate { get; }
protected override IEnumerable<object> EqualityKeys
{
get
{
yield return Target;
yield return ReflectedType;
yield return Name;
}
}
protected abstract Type[] ArgumentTypes { get; }
public SerializableDelegate(SerializationInfo info, StreamingContext context)
{
var reflectedType = (Type)info.GetValue(nameof(ReflectedType), typeof(Type));
var name = info.GetString(nameof(Name));
var target = info.GetValue(nameof(Target), reflectedType);
Delegate = Delegate.CreateDelegate(
typeof(TDelegate),
target,
reflectedType.GetMethod(name, ArgumentTypes));
}
public SerializableDelegate(Delegate action)
{
Delegate = action;
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(nameof(Target), Target, ReflectedType);
info.AddValue(nameof(ReflectedType), ReflectedType);
info.AddValue(nameof(Name), Name);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment