using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Manager.Exceptions; using Robust.Shared.Serialization.Markdown; using Robust.Shared.Serialization.Markdown.Mapping; using Robust.Shared.Serialization.Markdown.Sequence; using Robust.Shared.Serialization.Markdown.Value; namespace Robust.UnitTesting.Shared.Serialization.SerializationTests; [TestFixture] internal sealed partial class ManagerTests : OurSerializationTest { /* legend: NT => Nullable Type RT => Not Nullable Type NV => Null Value RV => Regular Value NS => Null source (copyto) NT => Null target (copyto) for each [write|read|copyto|copycreate] for each testcase except copyto, do: NT_NV NT_RV RT_NV //only possible for class RT_RV for copyto, do: NT_NS_NT NT_NS_RT NT_RS_NT NT_RS_RT RT_NS_NT //only possible for class RT_NS_RT //only possible for class RT_RS_NT //only possible for class RT_RS_RT testcases: array ISelfSerialize (only read/write) DataDefinitionSameType (struct, class) TypeSerializer (struct, reference) ContextTypeSerializer (struct, reference) enum todo DataDefinitionBaseType (struct, class) //means: should use !type todo returnsource (copy, createcopy) todo test the generic variants too //todo figure out a way to tell which serializer we used for (create)copy //todo datarecords //todo validate & pushcomposition //todo prioritytests ([copybyref] <> null <> custom <> regular <> array/enum <> datadef) */ #region DataSources public static IEnumerable ReadWriteTypesClass => new[] { new [] { new object[] { new ValueDataNode("testValue"), () => new SelfSerializeClass { TestValue = "testValue" }, () => new SelfSerializeClass { TestValue = "anotherTestValue" }, false, new Func[] { x => x.TestValue } }, //ISelfSerialize new object[] { new MappingDataNode(new Dictionary { { "one", new ValueDataNode("valueOne") }, { "two", new SequenceDataNode("2", "3") }, }){Tag = $"!type:{nameof(DataDefClass)}"}, () => (IDataDefBaseInterface)new DataDefClass { OneValue = "valueOne", TwoValue = new List { 2, 3 } }, () => (IDataDefBaseInterface)new DataDefClass { OneValue = "anotherValueOne", TwoValue = new List { 3, 4 } }, false, new Func[] { x => ((DataDefClass)x).OneValue, x => ((DataDefClass)x).TwoValue, } }, //DataDefinitionBaseType }, TestableTypesClass }.SelectMany(x => x); public static IEnumerable TestableTypesClass => new[] { new object[] { new SequenceDataNode("1", "2", "3"), () => new[] { 1, 2, 3 }, () => new[] { 3, 4, 5 }, false, new Func[] { x => x[0], x => x[1], x => x[2], } }, //array new object[] { new MappingDataNode(new Dictionary { { "one", new ValueDataNode("valueOne") }, { "two", new SequenceDataNode("2", "3") }, }), () => new DataDefClass { OneValue = "valueOne", TwoValue = new List { 2, 3 } }, () => new DataDefClass { OneValue = "anotherValueOne", TwoValue = new List { 3, 4 } }, false, new Func[] { x => x.OneValue, x => x.TwoValue, } }, //DataDefSameType new object[] { SerializerRanDataNode, (object)SerializerClass.SerializerReturn, (object)SerializerClass.SerializerReturnAlt, false, new Func[] { x => x.OneValue, x => x.TwoValue, } }, //TypeSerializer new object[] { SerializerRanCustomDataNode, (object)SerializerClass.SerializerCustomReturn, (object)SerializerClass.SerializerCustomReturnAlt, true, new Func[] { x => x.OneValue, x => x.TwoValue, } }, //CustomTypeSerializer }; public static IEnumerable ReadWriteTypesStruct => new[] { new object[][] { new object[] { new ValueDataNode("testValue"), () => new SelfSerializeStruct { TestValue = "testValue" }, () => new SelfSerializeStruct { TestValue = "anotherTestValue" }, false, new Func[] { x => x.TestValue } }, //ISelfSerialize /*new object[] { new MappingDataNode(new Dictionary { { new ValueDataNode("one"), new ValueDataNode("valueOne") }, { new ValueDataNode("two"), new SequenceDataNode("2", "3") }, }){Tag = $"!type:{nameof(DataDefStruct)}"}, () => new DataDefStruct { OneValue = "valueOne", TwoValue = new List { 2, 3 } } as IDataDefBaseInterface, () => new DataDefStruct { OneValue = "anotherValueOne", TwoValue = new List { 3, 4 } } as IDataDefBaseInterface, false, new Func[] { x => ((DataDefStruct)x).OneValue, x => ((DataDefStruct)x).TwoValue, } }, //DataDefinitionBaseType*/ }, TestableTypesStruct }.SelectMany(x => x!); public static IEnumerable TestableTypesStruct => new object[][] { new object[] { new MappingDataNode(new Dictionary() { { "one", new ValueDataNode("valueOne") }, { "two", new SequenceDataNode("2", "3") }, }), () => new DataDefStruct { OneValue = "valueOne", TwoValue = new List { 2, 3 } }, () => new DataDefStruct { OneValue = "anotherValueOne", TwoValue = new List { 3, 4 } }, false, new Func[] { x => x.OneValue, x => x.TwoValue, } }, //DataDefSameType new object[] { SerializerRanDataNode, (object)SerializerStruct.SerializerReturn, (object)SerializerStruct.SerializerReturnAlt, false, new Func[] { x => x.OneValue, x => x.TwoValue, } }, //TypeSerializer new object[] { SerializerRanCustomDataNode, (object)SerializerStruct.SerializerCustomReturn, (object)SerializerStruct.SerializerCustomReturnAlt, true, new Func[] { x => x.OneValue, x => x.TwoValue, } }, //CustomTypeSerializer new object[] { new ValueDataNode("C"), () => TestEnum.C, () => TestEnum.B, false, new Func[]{ x => x } } }; public static IEnumerable TestableTypesAll => new[] { TestableTypesClass, TestableTypesStruct }.SelectMany(x => x); public static IEnumerable ReadWriteTypesAll => new[] { ReadWriteTypesClass, ReadWriteTypesStruct }.SelectMany(x => x); #endregion #region Write [TestCaseSource(nameof(ReadWriteTypesStruct))] public void Write_NT_NV_Struct(DataNode _, Func __, Func ___, bool useContext, object[] ____) where T : struct { var resNode = Serialization.WriteValue(null, context: Context(useContext)); Assert.That(resNode.IsNull); } [TestCaseSource(nameof(ReadWriteTypesClass))] public void Write_NT_NV_Class(DataNode _, Func __, Func ___, bool useContext, object[] ____) where T : class { var resNode = Serialization.WriteValue(null, context: Context(useContext)); Assert.That(resNode.IsNull); } [TestCaseSource(nameof(ReadWriteTypesStruct))] public void Write_NT_RV_Struct(DataNode intendedNode, Func value, Func _, bool useContext, object[] __) where T : struct { var resNode = Serialization.WriteValue(value(), context: Context(useContext)); Assert.That(resNode.Equals(intendedNode)); } [TestCaseSource(nameof(ReadWriteTypesClass))] public void Write_NT_RV_Class(DataNode intendedNode, Func value, Func _, bool useContext, object[] __) where T : class { var resNode = Serialization.WriteValue(value(), context: Context(useContext)); Assert.That(resNode.Equals(intendedNode)); } [TestCaseSource(nameof(ReadWriteTypesClass))] public void Write_RT_NV_Class(DataNode intendedNode, Func value, Func altValue, bool useContext, object[] _) where T : class { Assert.That(() => Serialization.WriteValue(null!, context: Context(useContext), notNullableOverride: true), Throws.TypeOf()); } [TestCaseSource(nameof(ReadWriteTypesAll))] public void Write_RT_RV(DataNode intendedNode, Func value, Func altValue, bool useContext, object[] _) { var resNode = Serialization.WriteValue(value(), context: Context(useContext)); Assert.That(resNode.Equals(intendedNode)); } #endregion #region Read [TestCaseSource(nameof(ReadWriteTypesStruct))] public void Read_NT_NV_Struct(DataNode _, Func __, Func ___, bool useContext, object[] ____) where T : struct { var val = Serialization.Read(ValueDataNode.Null(), context: Context(useContext)); Assert.That(val, Is.Null); } [TestCaseSource(nameof(ReadWriteTypesClass))] public void Read_NT_NV_Class(DataNode _, Func __, Func ___, bool useContext, object[] ____) where T : class { var val = Serialization.Read(ValueDataNode.Null(), context: Context(useContext)); Assert.That(val, Is.Null); } [TestCaseSource(nameof(ReadWriteTypesStruct))] public void Read_NT_RV_Struct(DataNode node, Func value, Func _, bool useContext, object[] valueExtractors) where T : struct { var val = Serialization.Read(node, context: Context(useContext)); Assert.That(val, Is.Not.Null); AssertEqual(val!.Value, value(), valueExtractors); } [TestCaseSource(nameof(ReadWriteTypesClass))] public void Read_NT_RV_Class(DataNode node, Func value, Func _, bool useContext, object[] valueExtractors) where T : class { var val = Serialization.Read(node, context: Context(useContext)); Assert.That(val, Is.Not.Null); AssertEqual(val!, value(), valueExtractors); } [TestCaseSource(nameof(ReadWriteTypesAll))] public void Read_RT_NV(DataNode node, Func _, Func __, bool useContext, object[] ___) { Assert.That(() => Serialization.Read(ValueDataNode.Null(), context: Context(useContext), notNullableOverride: true), Throws.TypeOf()); } [TestCaseSource(nameof(ReadWriteTypesAll))] public void Read_RT_RV(DataNode node, Func value, Func __, bool useContext, object[] valueExtractors) { var val = Serialization.Read(node, context: Context(useContext)); AssertEqual(val!, value(), valueExtractors); } #endregion #region CopyTo [TestCaseSource(nameof(TestableTypesStruct))] public void CopyTo_NT_NS_NT_Struct(DataNode _, Func __, Func ___, bool useContext, object[] ____) where T : struct { T? target = null; Serialization.CopyTo(null, ref target, context: Context(useContext)); Assert.That(target, Is.Null); } [TestCaseSource(nameof(TestableTypesClass))] public void CopyTo_NT_NS_NT_Class(DataNode _, Func __, Func ___, bool useContext, object[] ____) where T : class { T? target = null; Serialization.CopyTo(null, ref target, context: Context(useContext)); Assert.That(target, Is.Null); } [TestCaseSource(nameof(TestableTypesStruct))] public void CopyTo_NT_NS_RT_Struct(DataNode _, Func value, Func __, bool useContext, object[] ___) where T : struct { T? target = value(); Serialization.CopyTo(null, ref target, context: Context(useContext)); Assert.That(target, Is.Null); } [TestCaseSource(nameof(TestableTypesClass))] public void CopyTo_NT_NS_RT_Class(DataNode _, Func value, Func __, bool useContext, object[] ___) where T : class { T? target = value(); Serialization.CopyTo(null, ref target, context: Context(useContext)); Assert.That(target, Is.Null); } [TestCaseSource(nameof(TestableTypesStruct))] public void CopyTo_NT_RS_NT_Struct(DataNode _, Func value, Func __, bool useContext, object[] valueExtractors) where T : struct { T? target = null; Serialization.CopyTo(value(), ref target, context: Context(useContext)); Assert.That(target, Is.Not.Null); AssertEqual(target!.Value, value(), valueExtractors); } [TestCaseSource(nameof(TestableTypesClass))] public void CopyTo_NT_RS_NT_Class(DataNode _, Func value, Func __, bool useContext, object[] valueExtractors) where T : class { T? target = null; Serialization.CopyTo(value(), ref target, context: Context(useContext)); Assert.That(target, Is.Not.Null); AssertEqual(target!, value(), valueExtractors); } [TestCaseSource(nameof(TestableTypesStruct))] public void CopyTo_NT_RS_RT_Struct(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) where T : struct { T? target = altValue(); Serialization.CopyTo(value(), ref target, context: Context(useContext)); Assert.That(target, Is.Not.Null); AssertEqual(target!.Value, value(), valueExtractors); } [TestCaseSource(nameof(TestableTypesClass))] public void CopyTo_NT_RS_RT_Class(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) where T : class { T? target = altValue(); Serialization.CopyTo(value(), ref target, context: Context(useContext)); Assert.That(target, Is.Not.Null); AssertEqual(target!, value(), valueExtractors); } [TestCaseSource(nameof(TestableTypesClass))] public void CopyTo_RT_NS_NT_Class(DataNode _, Func __, Func ___, bool useContext, object[] ____) where T : class { T target = null!; Assert.That(() => { Serialization.CopyTo(null!, ref target, context: Context(useContext), notNullableOverride: true); }, Throws.TypeOf()); } [TestCaseSource(nameof(TestableTypesClass))] public void CopyTo_RT_NS_RT_Class(DataNode _, Func value, Func __, bool useContext, object[] ___) where T : class { T? target = value(); Assert.That(() => { Serialization.CopyTo(null!, ref target, context: Context(useContext), notNullableOverride: true); }, Throws.TypeOf()); } [TestCaseSource(nameof(TestableTypesClass))] public void CopyTo_RT_RS_NT_Class(DataNode _, Func value, Func ___, bool useContext, object[] valueExtractors) where T : class { T target = null!; Serialization.CopyTo(value(), ref target, context: Context(useContext)); Assert.That(target, Is.Not.Null); AssertEqual(target!, value(), valueExtractors); } [TestCaseSource(nameof(TestableTypesAll))] public void CopyTo_RT_RS_RT(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) { var target = altValue(); Serialization.CopyTo(value(), ref target, context: Context(useContext)); AssertEqual(target!, value(), valueExtractors); } #endregion #region CreateCopy [TestCaseSource(nameof(TestableTypesStruct))] public void CreateCopy_NT_NV_Struct(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) where T : struct { var copy = Serialization.CreateCopy(null, context: Context(useContext)); Assert.That(copy, Is.Null); } [TestCaseSource(nameof(TestableTypesClass))] public void CreateCopy_NT_NV_Class(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) where T : class { var copy = Serialization.CreateCopy(null, context: Context(useContext)); Assert.That(copy, Is.Null); } [TestCaseSource(nameof(TestableTypesAll))] public void CreateCopy_NT_RV(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) { var copy = Serialization.CreateCopy(value(), context: Context(useContext)); Assert.That(copy, Is.Not.Null); AssertEqual(copy!, value(), valueExtractors); } [TestCaseSource(nameof(TestableTypesClass))] public void CreateCopy_RT_NV_Class(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) where T : class { Assert.That(() => { var __ = Serialization.CreateCopy(null!, context: Context(useContext), notNullableOverride: true); }, Throws.TypeOf()); } [TestCaseSource(nameof(TestableTypesAll))] public void CreateCopy_RT_RV(DataNode _, Func value, Func altValue, bool useContext, object[] valueExtractors) { var copy = Serialization.CreateCopy(value(), context: Context(useContext)); AssertEqual(copy, value(), valueExtractors); } #endregion #region CopyByRef [Test] public void CopyTo_CopyByRef_Class() { CopyByRefTestClass target = null!; var source = new CopyByRefTestClass(); Serialization.CopyTo(source, ref target, notNullableOverride: true); Assert.That(target, Is.Not.Null); Assert.That(target!, Is.SameAs(source)); } [Test] public void CopyTo_CopyByRef_NV_Class() { CopyByRefTestClass? target = null; Serialization.CopyTo(null, ref target); Assert.That(target, Is.Null); } [Test] public void CreateCopy_CopyByRef_Class() { var source = new CopyByRefTestClass(); var copy = Serialization.CreateCopy(source, notNullableOverride: true); Assert.That(copy, Is.SameAs(source)); } [Test] public void CreateCopy_CopyByRef_NV_Class() { var copy = Serialization.CreateCopy(null); Assert.That(copy, Is.Null); } [Test] public void CopyTo_CopyByRef_Struct() { CopyByRefTestStruct? target = null; var source = new CopyByRefTestStruct{ID = 5}; Serialization.CopyTo(source, ref target); Assert.That(target, Is.Not.Null); Assert.That(target!.Value.ID, Is.EqualTo(source.ID)); } [Test] public void CopyTo_CopyByRef_NV_Struct() { CopyByRefTestStruct? target = null; Serialization.CopyTo(null, ref target); Assert.That(target, Is.Null); } [Test] public void CreateCopy_CopyByRef_Struct() { var source = new CopyByRefTestStruct{ID = 5}; var copy = Serialization.CreateCopy(source); Assert.That(copy.ID, Is.EqualTo(source.ID)); } [Test] public void CreateCopy_CopyByRef_NV_Struct() { var copy = Serialization.CreateCopy(null); Assert.That(copy, Is.Null); } #endregion private void AssertEqual(T a, T b, object[] valueExtractors) { Assert.Multiple(() => { foreach (var extractor in valueExtractors.Cast>()) { var objA = extractor(a); var objB = extractor(b); Assert.That(objA.GetType(), Is.EqualTo(objB.GetType())); if (objA is not List listA) { Assert.That(extractor(a).Equals(extractor(b))); continue; } var listB = (List)objB; Assert.That(listA, Has.Count.EqualTo(listB.Count)); for (int i = 0; i < listA.Count; i++) { Assert.That(listA[i], Is.EqualTo(listB[i])); } } }); } private ISerializationContext? Context(bool useContext) => useContext ? this : null; }