From b82b763cc5833a9d929ccb95ad882bb7c64df10c Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 9 Apr 2019 06:17:16 +0300 Subject: [PATCH] Add UnitTests --- .../ZeroLevel.Discovery.csproj | 2 +- ...l.Discovery.csproj.CoreCompileInputs.cache | 2 +- ...el.Discovery.csprojAssemblyReference.cache | Bin 69748 -> 137473 bytes ZeroLevel.UnitTests/ArrayToolsTest.cs | 90 +++++ ZeroLevel.UnitTests/InvokingTest.cs | 165 +++++++++ ZeroLevel.UnitTests/MappingTest.cs | 207 +++++++++++ ZeroLevel.UnitTests/Models/BaseClass.cs | 14 + ZeroLevel.UnitTests/Models/BaseFakeClass.cs | 7 + ZeroLevel.UnitTests/Models/ChildClass.cs | 11 + ZeroLevel.UnitTests/Models/FakeClass.cs | 11 + ZeroLevel.UnitTests/Models/StaticFakeClass.cs | 11 + .../Properties/AssemblyInfo.cs | 20 ++ ZeroLevel.UnitTests/SerializationTests.cs | 335 ++++++++++++++++++ .../ZeroLevel.UnitTests.csproj | 82 +++++ ZeroLevel.UnitTests/packages.config | 5 + ZeroLevel.sln | 14 + .../Extensions/BitConverterExtensions.cs | 11 +- ZeroLevel/Services/Network/NetUtils.cs | 7 + .../Serialization/MemoryStreamReader.cs | 12 +- .../Serialization/MemoryStreamWriter.cs | 4 +- .../ZeroLevel.csproj.CoreCompileInputs.cache | 2 +- 21 files changed, 996 insertions(+), 16 deletions(-) create mode 100644 ZeroLevel.UnitTests/ArrayToolsTest.cs create mode 100644 ZeroLevel.UnitTests/InvokingTest.cs create mode 100644 ZeroLevel.UnitTests/MappingTest.cs create mode 100644 ZeroLevel.UnitTests/Models/BaseClass.cs create mode 100644 ZeroLevel.UnitTests/Models/BaseFakeClass.cs create mode 100644 ZeroLevel.UnitTests/Models/ChildClass.cs create mode 100644 ZeroLevel.UnitTests/Models/FakeClass.cs create mode 100644 ZeroLevel.UnitTests/Models/StaticFakeClass.cs create mode 100644 ZeroLevel.UnitTests/Properties/AssemblyInfo.cs create mode 100644 ZeroLevel.UnitTests/SerializationTests.cs create mode 100644 ZeroLevel.UnitTests/ZeroLevel.UnitTests.csproj create mode 100644 ZeroLevel.UnitTests/packages.config diff --git a/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj b/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj index 8d11bee..1770242 100644 --- a/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj +++ b/ZeroLevel.Discovery/ZeroLevel.Discovery.csproj @@ -43,7 +43,7 @@ ..\packages\Microsoft.Owin.Hosting.4.0.1\lib\net45\Microsoft.Owin.Hosting.dll - C:\Users\a.bozhenov\source\repos\ZeroTests\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\Owin.1.0\lib\net40\Owin.dll diff --git a/ZeroLevel.Discovery/obj/Debug/ZeroLevel.Discovery.csproj.CoreCompileInputs.cache b/ZeroLevel.Discovery/obj/Debug/ZeroLevel.Discovery.csproj.CoreCompileInputs.cache index e40d599..ed9d14b 100644 --- a/ZeroLevel.Discovery/obj/Debug/ZeroLevel.Discovery.csproj.CoreCompileInputs.cache +++ b/ZeroLevel.Discovery/obj/Debug/ZeroLevel.Discovery.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -baf2f9d75e4982e81a51362dec180283f7786a6a +affe24c0b0d3f87817c2fa3318e87e4ecf28560f diff --git a/ZeroLevel.Discovery/obj/Debug/ZeroLevel.Discovery.csprojAssemblyReference.cache b/ZeroLevel.Discovery/obj/Debug/ZeroLevel.Discovery.csprojAssemblyReference.cache index d2dc83f9ee8c0a1e3d517789b4948864d44bad63..e6bf7b18427170d91fe0593845ff2c42ec32586a 100644 GIT binary patch literal 137473 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 Nt!Nr&1nj?h{6GK31gii5 diff --git a/ZeroLevel.UnitTests/ArrayToolsTest.cs b/ZeroLevel.UnitTests/ArrayToolsTest.cs new file mode 100644 index 0000000..fd21dd6 --- /dev/null +++ b/ZeroLevel.UnitTests/ArrayToolsTest.cs @@ -0,0 +1,90 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using ZeroLevel; + +namespace ZeroArrayExtensionsTest +{ + [TestClass] + public class ArrayExtensionsTest + { + internal class EQDTO + { + public byte[] Arr; + public DateTime Date; + public string Title; + public long Num; + + public override bool Equals(object obj) + { + return this.Equals(obj as EQDTO); + } + + public bool Equals(EQDTO other) + { + if (other == null) return false; + + if (ArrayExtensions.UnsafeEquals(this.Arr, other.Arr) == false) return false; + if (DateTime.Compare(this.Date, other.Date) != 0) return false; + if (string.Compare(this.Title, other.Title, StringComparison.OrdinalIgnoreCase) != 0) return false; + return this.Num == other.Num; + } + } + + [TestMethod] + public void ByteArrayEqualTest() + { + // Arrange + var a1 = new byte[] { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 7 }; + var a2 = new byte[] { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 7 }; + var a3 = new byte[] { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9 }; + var a4 = new byte[] { 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 7 }; + byte[] a5 = null; + byte[] a6 = null; + var a7 = new byte[0]; + var a8 = new byte[0]; + + // Assert + Assert.IsTrue(ArrayExtensions.UnsafeEquals(a1, a2)); + Assert.IsTrue(ArrayExtensions.UnsafeEquals(a5, a6)); + Assert.IsTrue(ArrayExtensions.UnsafeEquals(a7, a8)); + + Assert.IsFalse(ArrayExtensions.UnsafeEquals(a1, a3)); + Assert.IsFalse(ArrayExtensions.UnsafeEquals(a1, a4)); + + Assert.IsFalse(ArrayExtensions.UnsafeEquals(a1, a5)); + Assert.IsFalse(ArrayExtensions.UnsafeEquals(a1, a7)); + + Assert.IsFalse(ArrayExtensions.UnsafeEquals(a5, a7)); + } + + [TestMethod] + public void ArrayEqualTest() + { + // Arrange + var date = DateTime.Now; + var arr1 = new EQDTO[] + { + new EQDTO { Arr = new byte[] { 1,2,3}, Date = date, Num = 10, Title = "t1" }, + new EQDTO { Arr = new byte[] { 1,2,4}, Date = date, Num = 20, Title = "t2" }, + new EQDTO { Arr = new byte[] { 1,2,5}, Date = date, Num = 30, Title = "t3" }, + new EQDTO { Arr = new byte[] { 1,2,6}, Date = date, Num = 40, Title = "t4" }, + new EQDTO { Arr = new byte[] { 1,2,7}, Date = date, Num = 50, Title = "t5" } + }; + var arr2 = new EQDTO[] + { + new EQDTO { Arr = new byte[] { 1,2,6}, Date = date, Num = 40, Title = "t4" }, + new EQDTO { Arr = new byte[] { 1,2,7}, Date = date, Num = 50, Title = "t5" } + }; + var arr3 = new EQDTO[] + { + new EQDTO { Arr = new byte[] { 1,2,6}, Date = date, Num = 40, Title = "t4" }, + new EQDTO { Arr = new byte[] { 1,2,7}, Date = date, Num = 50, Title = "t5" }, + new EQDTO { Arr = new byte[] { 1,2,7}, Date = date, Num = 50, Title = "t6" }, + }; + + //Assert + Assert.IsTrue(ArrayExtensions.Contains(arr1, arr2)); + Assert.IsFalse(ArrayExtensions.Contains(arr1, arr3)); + } + } +} diff --git a/ZeroLevel.UnitTests/InvokingTest.cs b/ZeroLevel.UnitTests/InvokingTest.cs new file mode 100644 index 0000000..4b170a6 --- /dev/null +++ b/ZeroLevel.UnitTests/InvokingTest.cs @@ -0,0 +1,165 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Reflection; +using ZeroInvokingTest.Models; +using ZeroLevel.Services.Invokation; + +namespace ZeroInvokingTest +{ + [TestClass] + public class InvokingTest + { + [TestMethod] + public void InvokeTypeAllMethod() + { + // Arrange + var invoker = InvokeWrapper.Create(); + // Act + invoker.Configure(); + var identityGetString = invoker.GetInvokerIdentity("GetString", new Type[] { typeof(string) }); + var identityGetNumber = invoker.GetInvokerIdentity("GetNumber", new Type[] { typeof(int) }); + var identityGetDateTime = invoker.GetInvokerIdentity("GetDateTime", new Type[] { typeof(DateTime) }); + var identityGetHelp = invoker.GetInvokerIdentity("GetHelp", null); + // Assert + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetString, new object[] { "hello" }), "hello"); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetNumber, new object[] { 100 }), 100); + var date = DateTime.Now; + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetDateTime, new object[] { date }), date); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetHelp), "help"); + } + + [TestMethod] + public void InvokeTypeMethodByName() + { + // Arrange + var invoker = InvokeWrapper.Create(); + // Act + //invoker.Configure("GetString"); + invoker.Configure("GetHelp"); + invoker.Configure("GetNumber"); + invoker.Configure("GetDateTime"); + var identityGetString = invoker.GetInvokerIdentity("GetString", new Type[] { typeof(string) }); + var identityGetNumber = invoker.GetInvokerIdentity("GetNumber", new Type[] { typeof(int) }); + var identityGetDateTime = invoker.GetInvokerIdentity("GetDateTime", new Type[] { typeof(DateTime) }); + var identityGetHelp = invoker.GetInvokerIdentity("GetHelp", null); + // Assert + try + { + var obj = invoker.Invoke(new FakeClass(), identityGetString, new object[] { "hello" }); + Assert.Fail("An exception should have been thrown"); + } + catch { } + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetNumber, new object[] { 100 }), 100); + var date = DateTime.Now; + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetDateTime, new object[] { date }), date); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetHelp), "help"); + } + + [TestMethod] + public void InvokeTypeMethodByFilter() + { + // Arrange + var invoker = InvokeWrapper.Create(); + // Act + invoker.Configure(m => m.Name.Equals("GetHelp") || m.Name.Equals("GetNumber")); + var identityGetString = invoker.GetInvokerIdentity("GetString", new Type[] { typeof(string) }); + var identityGetNumber = invoker.GetInvokerIdentity("GetNumber", new Type[] { typeof(int) }); + var identityGetDateTime = invoker.GetInvokerIdentity("GetDateTime", new Type[] { typeof(DateTime) }); + var identityGetHelp = invoker.GetInvokerIdentity("GetHelp", null); + // Assert + try + { + var obj = invoker.Invoke(new FakeClass(), identityGetString, new object[] { "hello" }); + Assert.Fail("An exception should have been thrown"); + } + catch { } + try + { + var date = DateTime.Now; + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetDateTime, new object[] { date }), date); + Assert.Fail("An exception should have been thrown"); + } + catch { } + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetNumber, new object[] { 100 }), 100); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetHelp), "help"); + } + + [TestMethod] + public void InvokeByMethodsList() + { + // Arrange + var invoker = InvokeWrapper.Create(); + // Act + invoker.Configure(new MethodInfo[] + { + typeof(FakeClass).GetMethod("GetHelp", BindingFlags.Public | BindingFlags.FlattenHierarchy| BindingFlags.Instance), + typeof(FakeClass).GetMethod("GetNumber", BindingFlags.NonPublic| BindingFlags.Instance), + typeof(FakeClass).GetMethod("GetDateTime", BindingFlags.NonPublic| BindingFlags.Instance), + typeof(FakeClass).GetMethod("GetString") + }); + var identityGetString = invoker.GetInvokerIdentity("GetString", new Type[] { typeof(string) }); + var identityGetNumber = invoker.GetInvokerIdentity("GetNumber", new Type[] { typeof(int) }); + var identityGetDateTime = invoker.GetInvokerIdentity("GetDateTime", new Type[] { typeof(DateTime) }); + var identityGetHelp = invoker.GetInvokerIdentity("GetHelp", null); + // Assert + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetString, new object[] { "hello" }), "hello"); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetNumber, new object[] { 100 }), 100); + var date = DateTime.Now; + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetDateTime, new object[] { date }), date); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetHelp), "help"); + } + + [TestMethod] + public void InvokeByMethods() + { + // Arrange + var invoker = InvokeWrapper.Create(); + // Act + invoker.Configure(typeof(FakeClass).GetMethod("GetHelp", BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance)); + invoker.Configure(typeof(FakeClass).GetMethod("GetNumber", BindingFlags.NonPublic | BindingFlags.Instance)); + invoker.Configure(typeof(FakeClass).GetMethod("GetDateTime", BindingFlags.NonPublic | BindingFlags.Instance)); + invoker.Configure(typeof(FakeClass).GetMethod("GetString")); + var identityGetString = invoker.GetInvokerIdentity("GetString", new Type[] { typeof(string) }); + var identityGetNumber = invoker.GetInvokerIdentity("GetNumber", new Type[] { typeof(int) }); + var identityGetDateTime = invoker.GetInvokerIdentity("GetDateTime", new Type[] { typeof(DateTime) }); + var identityGetHelp = invoker.GetInvokerIdentity("GetHelp", null); + // Assert + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetString, new object[] { "hello" }), "hello"); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetNumber, new object[] { 100 }), 100); + var date = DateTime.Now; + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetDateTime, new object[] { date }), date); + Assert.AreEqual(invoker.Invoke(new FakeClass(), identityGetHelp), "help"); + } + + [TestMethod] + public void InvokeStaticByMethods() + { + // Arrange + var invoker = InvokeWrapper.Create(); + // Act + invoker.Configure(typeof(StaticFakeClass).GetMethod("GetNumber", BindingFlags.NonPublic | BindingFlags.Static)); + invoker.Configure(typeof(StaticFakeClass).GetMethod("GetDateTime", BindingFlags.NonPublic | BindingFlags.Static)); + invoker.Configure(typeof(StaticFakeClass).GetMethod("GetString")); + var identityGetString = invoker.GetInvokerIdentity("GetString", new Type[] { typeof(string) }); + var identityGetNumber = invoker.GetInvokerIdentity("GetNumber", new Type[] { typeof(int) }); + var identityGetDateTime = invoker.GetInvokerIdentity("GetDateTime", new Type[] { typeof(DateTime) }); + // Assert + Assert.AreEqual(invoker.InvokeStatic(identityGetString, new object[] { "hello" }), "hello"); + Assert.AreEqual(invoker.InvokeStatic(identityGetNumber, new object[] { 100 }), 100); + var date = DateTime.Now; + Assert.AreEqual(invoker.InvokeStatic(identityGetDateTime, new object[] { date }), date); + } + + [TestMethod] + public void InvokeByDelegate() + { + // Arrange + var invoker = InvokeWrapper.Create(); + // Act + var func = new Func(str => str.Length > 0); + var name = invoker.Configure(func); + // Assert + Assert.IsTrue((bool)invoker.Invoke(func.Target, name, new object[] { "hello" })); + } + } +} diff --git a/ZeroLevel.UnitTests/MappingTest.cs b/ZeroLevel.UnitTests/MappingTest.cs new file mode 100644 index 0000000..02fe775 --- /dev/null +++ b/ZeroLevel.UnitTests/MappingTest.cs @@ -0,0 +1,207 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ZeroLevel.Services.ObjectMapping; +using ZeroMappingTest.Models; +using System.Collections.Generic; + +namespace ZeroMappingTest +{ + [TestClass] + public class MappingTest + { + [TestMethod] + public void TestAbstractClassGetInfo() + { + // Arrange + var mapper = TypeMapper.Create(); + // Act + var list = new List(); + mapper.TraversalMembers(f => list.Add(f.Name)); + // Assert + Assert.IsTrue(mapper.Exists("Id")); + Assert.IsTrue(mapper.Exists("Title")); + Assert.IsTrue(mapper.Exists("Description")); + + Assert.IsTrue(list.Contains("Id")); + Assert.IsTrue(list.Contains("Title")); + Assert.IsTrue(list.Contains("Description")); + + Assert.IsFalse(mapper.Exists("Version")); + Assert.IsFalse(mapper.Exists("Created")); + + Assert.IsFalse(list.Contains("Version")); + Assert.IsFalse(list.Contains("Created")); + + Assert.AreEqual(mapper.EntityType, typeof(BaseClass)); + } + + [TestMethod] + public void TestInheritedClassGetInfo() + { + // Arrange + var mapper = TypeMapper.Create(); + // Act + var list = new List(); + mapper.TraversalMembers(f => list.Add(f.Name)); + // Assert + Assert.IsTrue(mapper.Exists("Id")); + Assert.IsTrue(mapper.Exists("Title")); + Assert.IsTrue(mapper.Exists("Description")); + Assert.IsTrue(mapper.Exists("Number")); + Assert.IsTrue(mapper.Exists("Balance")); + Assert.IsTrue(mapper.Exists("ReadOnlyProperty")); + Assert.IsTrue(mapper.Exists("WriteOnlyProperty")); + + Assert.IsTrue(list.Contains("Id")); + Assert.IsTrue(list.Contains("Title")); + Assert.IsTrue(list.Contains("Description")); + Assert.IsTrue(list.Contains("Number")); + Assert.IsTrue(list.Contains("Balance")); + Assert.IsTrue(list.Contains("ReadOnlyProperty")); + Assert.IsTrue(list.Contains("WriteOnlyProperty")); + + Assert.IsFalse(mapper.Exists("HiddenField")); + Assert.IsFalse(mapper.Exists("Version")); + Assert.IsFalse(mapper.Exists("Created")); + + Assert.IsFalse(list.Contains("HiddenField")); + Assert.IsFalse(list.Contains("Version")); + Assert.IsFalse(list.Contains("Created")); + + Assert.AreEqual(mapper.EntityType, typeof(ChildClass)); + } + + [TestMethod] + public void TestAbstractClassMapping() + { + // Arrange + var instance = new ChildClass + { + Id = Guid.Empty, + Title = "title", + Description = "description", + WriteOnlyProperty = 100, + Balance = 100, + Number = 100 + }; + var mapper = TypeMapper.Create(); + // Act + var id = Guid.NewGuid(); + var title = "New title"; + var description = "New description"; + mapper.Set(instance, "Id", id); + mapper.Set(instance, "Title", title); + mapper.Set(instance, "Description", description); + // Assert + Assert.AreEqual(mapper.Get(instance, "Id"), id); + Assert.AreEqual(mapper.Get(instance, "Title"), title); + Assert.AreEqual(mapper.Get(instance, "Description"), description); + + Assert.AreEqual(mapper.Get(instance, "Id"), id); + Assert.AreEqual(mapper.Get(instance, "Title"), title); + Assert.AreEqual(mapper.Get(instance, "Description"), description); + try + { + mapper.Get(instance, "Number"); + Assert.Fail("Must be inaccessability"); + } + catch + { + } + } + + [TestMethod] + public void TestInheritedClassMapping() + { + // Arrange + var instance = new ChildClass + { + Id = Guid.Empty, + Title = "title", + Description = "description", + WriteOnlyProperty = 100, + Balance = 100, + Number = 100 + }; + var mapper = TypeMapper.Create(); + // Act + var id = Guid.NewGuid(); + var title = "New title"; + var description = "New description"; + var number = 5465; + var balance = 5555; + + + mapper.Set(instance, "Id", id); + mapper.Set(instance, "Title", title); + mapper.Set(instance, "Description", description); + mapper.Set(instance, "Number", number); + mapper.Set(instance, "Balance", balance); + // Assert + Assert.AreEqual(mapper.Get(instance, "Id"), id); + Assert.AreEqual(mapper.Get(instance, "Title"), title); + Assert.AreEqual(mapper.Get(instance, "Description"), description); + Assert.AreEqual(mapper.Get(instance, "Number"), number); + Assert.AreEqual(mapper.Get(instance, "Balance"), balance); + + Assert.AreEqual(mapper.Get(instance, "Id"), id); + Assert.AreEqual(mapper.Get(instance, "Title"), title); + Assert.AreEqual(mapper.Get(instance, "Description"), description); + Assert.AreEqual(mapper.Get(instance, "Number"), number); + Assert.AreEqual(mapper.Get(instance, "Balance"), balance); + + try + { + var test = 1000; + mapper.Set(instance, "ReadOnlyProperty", test); + Assert.Fail("There should be no possibility to set a value."); + } + catch + { + + } + + try + { + mapper.Get(instance, "WriteOnlyProperty"); + Assert.Fail("There should be no possibility to get a value."); + } + catch + { + + } + + try + { + mapper.GetOrDefault(instance, "WriteOnlyProperty", null); + } + catch + { + Assert.Fail("It should be possible to get the default value."); + } + + try + { + mapper.Get(instance, "Number"); + } + catch(Exception ex) + { + Assert.Fail(ex.Message); + } + } + + [TestMethod] + public void TestMapperscaching() + { + // Arrange + var mapper1 = TypeMapper.Create(); + var mapper2 = TypeMapper.Create(); + var mapper3 = TypeMapper.Create(false); + // Act + // Assert + Assert.AreSame(mapper1, mapper2); + Assert.AreNotSame(mapper1, mapper3); + Assert.AreNotSame(mapper3, mapper2); + } + } +} diff --git a/ZeroLevel.UnitTests/Models/BaseClass.cs b/ZeroLevel.UnitTests/Models/BaseClass.cs new file mode 100644 index 0000000..339cc8a --- /dev/null +++ b/ZeroLevel.UnitTests/Models/BaseClass.cs @@ -0,0 +1,14 @@ +using System; + +namespace ZeroMappingTest.Models +{ + public abstract class BaseClass + { + public Guid Id; + public string Title { get; set; } + public string Description { get; set; } + + protected long Version { get; set; } + private DateTime Created { get; set; } + } +} diff --git a/ZeroLevel.UnitTests/Models/BaseFakeClass.cs b/ZeroLevel.UnitTests/Models/BaseFakeClass.cs new file mode 100644 index 0000000..2eb89ec --- /dev/null +++ b/ZeroLevel.UnitTests/Models/BaseFakeClass.cs @@ -0,0 +1,7 @@ +namespace ZeroInvokingTest.Models +{ + public abstract class BaseFakeClass + { + public string GetHelp() => "help"; + } +} diff --git a/ZeroLevel.UnitTests/Models/ChildClass.cs b/ZeroLevel.UnitTests/Models/ChildClass.cs new file mode 100644 index 0000000..6c76e16 --- /dev/null +++ b/ZeroLevel.UnitTests/Models/ChildClass.cs @@ -0,0 +1,11 @@ +namespace ZeroMappingTest.Models +{ + public class ChildClass: + BaseClass + { + public int Number; + public int Balance { get; set; } + public int ReadOnlyProperty { get { return Number; } } + public int WriteOnlyProperty { set { Number = value; } } + } +} diff --git a/ZeroLevel.UnitTests/Models/FakeClass.cs b/ZeroLevel.UnitTests/Models/FakeClass.cs new file mode 100644 index 0000000..5aa547c --- /dev/null +++ b/ZeroLevel.UnitTests/Models/FakeClass.cs @@ -0,0 +1,11 @@ +using System; + +namespace ZeroInvokingTest.Models +{ + public class FakeClass : BaseFakeClass + { + public string GetString(string line) => line; + internal int GetNumber(int number) => number; + private DateTime GetDateTime(DateTime date) => date; + } +} diff --git a/ZeroLevel.UnitTests/Models/StaticFakeClass.cs b/ZeroLevel.UnitTests/Models/StaticFakeClass.cs new file mode 100644 index 0000000..2386e5b --- /dev/null +++ b/ZeroLevel.UnitTests/Models/StaticFakeClass.cs @@ -0,0 +1,11 @@ +using System; + +namespace ZeroInvokingTest.Models +{ + public static class StaticFakeClass + { + public static string GetString(string line) => line; + internal static int GetNumber(int number) => number; + private static DateTime GetDateTime(DateTime date) => date; + } +} diff --git a/ZeroLevel.UnitTests/Properties/AssemblyInfo.cs b/ZeroLevel.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fb56531 --- /dev/null +++ b/ZeroLevel.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("ZeroLevel.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ZeroLevel.UnitTests")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("c500b489-c432-499d-b0e4-b41998b12c49")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ZeroLevel.UnitTests/SerializationTests.cs b/ZeroLevel.UnitTests/SerializationTests.cs new file mode 100644 index 0000000..ea6ff88 --- /dev/null +++ b/ZeroLevel.UnitTests/SerializationTests.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ZeroLevel.Network; +using ZeroLevel.Services.Serialization; + +namespace ZeroLevel.UnitTests +{ + [TestClass] + public class SerializationTests + { + private static bool TestOrderingEquals(IEnumerable A, IEnumerable B, Func comparer) + { + if (A == null && B == null) return true; + if (A == null || B == null) return false; + if (A.Count() != B.Count()) return false; + var enumA = A.GetEnumerator(); + var enumB = B.GetEnumerator(); + while (enumA.MoveNext() && enumB.MoveNext()) + { + if (enumA.Current == null && enumB.Current == null) continue; + if (comparer(enumA.Current, enumB.Current) == false) return false; + } + return true; + } + + private void MakePrimitiveTest(T value, Func comparator = null) + { + // Act + var data = MessageSerializer.SerializeCompatible(value); + var clone = MessageSerializer.DeserializeCompatible(data); + + // Assert + if (comparator == null) + { + Assert.AreEqual(value, clone); + } + else + { + Assert.IsTrue(comparator(value, clone)); + } + } + + private void MakeCollectionTest(IEnumerable value, Func comparator = null) + { + // Act + var data = MessageSerializer.SerializeCompatible>(value); + var clone = MessageSerializer.DeserializeCompatible>(data); + + // Assert + if (value == null && clone != null && !clone.Any()) return; // OK + if (comparator == null) + { + Assert.IsTrue(CollectionComparsionExtensions.OrderingEquals(value, clone)); + } + else + { + Assert.IsTrue(TestOrderingEquals(value, clone, comparator)); + } + } + + [TestMethod] + public void SerializeDateTime() + { + MakePrimitiveTest(DateTime.Now); + MakePrimitiveTest(DateTime.UtcNow); + MakePrimitiveTest(DateTime.Today); + MakePrimitiveTest(DateTime.Now.AddYears(2000)); + MakePrimitiveTest(DateTime.MinValue); + MakePrimitiveTest(DateTime.MaxValue); + } + + [TestMethod] + public void SerializeIPAddress() + { + var comparator = new Func((left, right) => NetUtils.Compare(left, right) == 0); + MakePrimitiveTest(IPAddress.Any, comparator); + MakePrimitiveTest(IPAddress.Broadcast, comparator); + MakePrimitiveTest(IPAddress.IPv6Any, comparator); + MakePrimitiveTest(IPAddress.IPv6Loopback, comparator); + MakePrimitiveTest(IPAddress.IPv6None, comparator); + MakePrimitiveTest(IPAddress.Loopback, comparator); + MakePrimitiveTest(IPAddress.None, comparator); + MakePrimitiveTest(IPAddress.Parse("93.111.16.58"), comparator); + } + + [TestMethod] + public void SerializeIPEndPoint() + { + var comparator = new Func((left, right) => NetUtils.Compare(left, right) == 0); + MakePrimitiveTest(new IPEndPoint(IPAddress.Any, 1), comparator); + MakePrimitiveTest(new IPEndPoint(IPAddress.Broadcast, 600), comparator); + MakePrimitiveTest(new IPEndPoint(IPAddress.IPv6Any, IPEndPoint.MaxPort), comparator); + MakePrimitiveTest(new IPEndPoint(IPAddress.IPv6Loopback, 8080), comparator); + MakePrimitiveTest(new IPEndPoint(IPAddress.IPv6None, 80), comparator); + MakePrimitiveTest(new IPEndPoint(IPAddress.Loopback, 9000), comparator); + MakePrimitiveTest(new IPEndPoint(IPAddress.None, 0), comparator); + MakePrimitiveTest(new IPEndPoint(IPAddress.Parse("93.111.16.58"), IPEndPoint.MinPort), comparator); + } + + [TestMethod] + public void SerializeGuid() + { + MakePrimitiveTest(Guid.Empty); + MakePrimitiveTest(Guid.NewGuid()); + } + + [TestMethod] + public void SerializeTimeSpan() + { + MakePrimitiveTest(TimeSpan.MaxValue); + MakePrimitiveTest(TimeSpan.MinValue); + MakePrimitiveTest(TimeSpan.Zero); + MakePrimitiveTest(TimeSpan.FromDays(1024)); + MakePrimitiveTest(TimeSpan.FromMilliseconds(1)); + MakePrimitiveTest(TimeSpan.FromTicks(1)); + MakePrimitiveTest(TimeSpan.FromTicks(0)); + } + + [TestMethod] + public void SerializeString() + { + var comparator = new Func((left, right) => + (left == null && right == null) || + (left == null && right != null && right.Length == 0) || + (left != null && left.Length == 0 && right == null) || + string.Compare(left, right, StringComparison.InvariantCulture) == 0); + MakePrimitiveTest("", comparator); + MakePrimitiveTest(String.Empty, comparator); + MakePrimitiveTest(null, comparator); + MakePrimitiveTest("HELLO!", comparator); + MakePrimitiveTest("𐌼𐌰𐌲 𐌲𐌻𐌴𐍃 𐌹̈𐍄𐌰𐌽, 𐌽𐌹 𐌼𐌹𐍃 𐍅𐌿 𐌽𐌳𐌰𐌽 𐌱𐍂𐌹𐌲𐌲𐌹𐌸", comparator); + } + + [TestMethod] + public void SerializeInt32() + { + MakePrimitiveTest(-0); + MakePrimitiveTest(0); + MakePrimitiveTest(-10); + MakePrimitiveTest(10); + MakePrimitiveTest(Int32.MinValue); + MakePrimitiveTest(Int32.MaxValue); + } + + [TestMethod] + public void SerializeInt64() + { + MakePrimitiveTest(-0); + MakePrimitiveTest(0); + MakePrimitiveTest(-10); + MakePrimitiveTest(10); + MakePrimitiveTest(Int64.MinValue); + MakePrimitiveTest(Int64.MaxValue); + MakePrimitiveTest(Int64.MinValue / 2); + MakePrimitiveTest(Int64.MaxValue / 2); + } + + [TestMethod] + public void SerializeDecimal() + { + MakePrimitiveTest(-0); + MakePrimitiveTest(0); + MakePrimitiveTest(-10); + MakePrimitiveTest(10); + MakePrimitiveTest(Decimal.MinValue); + MakePrimitiveTest(Decimal.MaxValue); + MakePrimitiveTest(Decimal.MinValue / 2); + MakePrimitiveTest(Decimal.MaxValue / 2); + } + + [TestMethod] + public void SerializeFloat() + { + MakePrimitiveTest(-0); + MakePrimitiveTest(0); + MakePrimitiveTest(-10); + MakePrimitiveTest(10); + MakePrimitiveTest(float.MinValue); + MakePrimitiveTest(float.MaxValue); + MakePrimitiveTest(float.MinValue / 2); + MakePrimitiveTest(float.MaxValue / 2); + } + + [TestMethod] + public void SerializeDouble() + { + MakePrimitiveTest(-0); + MakePrimitiveTest(0); + MakePrimitiveTest(-10); + MakePrimitiveTest(10); + MakePrimitiveTest(Double.MinValue); + MakePrimitiveTest(Double.MaxValue); + MakePrimitiveTest(Double.MinValue / 2); + MakePrimitiveTest(Double.MaxValue / 2); + } + + [TestMethod] + public void SerializeBoolean() + { + MakePrimitiveTest(true); + MakePrimitiveTest(false); + } + + [TestMethod] + public void SerializeByte() + { + MakePrimitiveTest(0); + MakePrimitiveTest(-0); + MakePrimitiveTest(1); + MakePrimitiveTest(10); + MakePrimitiveTest(128); + MakePrimitiveTest(255); + } + + [TestMethod] + public void SerializeBytes() + { + var comparator = new Func((left, right) => + (left == null && (right == null || right.Length == 0)) || ArrayExtensions.UnsafeEquals(left, right)); + MakePrimitiveTest(null, comparator); + MakePrimitiveTest(new byte[] { }, comparator); + MakePrimitiveTest(new byte[] { 1 }, comparator); + MakePrimitiveTest(new byte[] { 0, 1, 10, 100, 128, 255 }, comparator); + } + + /* + COLLECTIONS + */ + + [TestMethod] + public void SerializeCollectionDateTime() + { + MakeCollectionTest(null); + MakeCollectionTest(new DateTime[] { }); + MakeCollectionTest(new DateTime[] { DateTime.Now, DateTime.UtcNow, DateTime.Today, DateTime.Now.AddYears(2000), DateTime.MinValue, DateTime.MaxValue }); + } + + [TestMethod] + public void SerializeCollectionIPAddress() + { + var comparator = new Func((left, right) => NetUtils.Compare(left, right) == 0); + MakeCollectionTest(null); + MakeCollectionTest(new IPAddress[] { IPAddress.Any, IPAddress.Broadcast, IPAddress.IPv6Any, IPAddress.IPv6Loopback, IPAddress.IPv6None, IPAddress.Loopback, IPAddress.None, IPAddress.Parse("93.111.16.58") }, comparator); + } + + [TestMethod] + public void SerializeCollectionIPEndPoint() + { + var comparator = new Func((left, right) => NetUtils.Compare(left, right) == 0); + MakeCollectionTest(null); + MakeCollectionTest(new IPEndPoint[] { }); + MakeCollectionTest(new IPEndPoint[] { new IPEndPoint(IPAddress.Any, 1), new IPEndPoint(IPAddress.Broadcast, 600), new IPEndPoint(IPAddress.IPv6Any, IPEndPoint.MaxPort), new IPEndPoint(IPAddress.IPv6Loopback, 8080), new IPEndPoint(IPAddress.IPv6None, 80), new IPEndPoint(IPAddress.Loopback, 9000), new IPEndPoint(IPAddress.None, 0), new IPEndPoint(IPAddress.Parse("93.111.16.58"), IPEndPoint.MinPort) }, comparator); + } + + [TestMethod] + public void SerializeCollectionGuid() + { + MakeCollectionTest(null); + MakeCollectionTest(new Guid[] { }); + MakeCollectionTest(new Guid[] { Guid.Empty, Guid.NewGuid() }); + } + + [TestMethod] + public void SerializeCollectionTimeSpan() + { + MakeCollectionTest(new TimeSpan[] { TimeSpan.MaxValue, TimeSpan.MinValue, TimeSpan.Zero, TimeSpan.FromDays(1024), TimeSpan.FromMilliseconds(1), TimeSpan.FromTicks(1), TimeSpan.FromTicks(0) }); + } + + [TestMethod] + public void SerializeCollectionString() + { + var comparator = new Func((left, right) => + (left == null && right == null) || + (left == null && right != null && right.Length == 0) || + (left != null && left.Length == 0 && right == null) || + string.Compare(left, right, StringComparison.InvariantCulture) == 0); + MakeCollectionTest(new string[] { "", String.Empty, null, "HELLO!", "𐌼𐌰𐌲 𐌲𐌻𐌴𐍃 𐌹̈𐍄𐌰𐌽, 𐌽𐌹 𐌼𐌹𐍃 𐍅𐌿 𐌽𐌳𐌰𐌽 𐌱𐍂𐌹𐌲𐌲𐌹𐌸" }, comparator); + } + + + [TestMethod] + public void SerializeCollectionInt32() + { + MakeCollectionTest(new int[] { -0, 0, -10, 10, Int32.MinValue, Int32.MaxValue }); + } + + [TestMethod] + public void SerializeCollectionInt64() + { + MakeCollectionTest(new long[] { -0, 0, -10, 10, Int64.MinValue, Int64.MaxValue, Int64.MinValue / 2, Int64.MaxValue / 2 }); + } + + [TestMethod] + public void SerializeCollectionDecimal() + { + MakeCollectionTest(new Decimal[] { -0, 0, -10, 10, Decimal.MinValue, Decimal.MaxValue, Decimal.MinValue / 2, Decimal.MaxValue / 2 }); + } + + [TestMethod] + public void SerializeCollectionFloat() + { + MakeCollectionTest(new float[] { -0, 0, -10, 10, float.MinValue, float.MaxValue, float.MinValue / 2, float.MaxValue / 2 }); + } + + [TestMethod] + public void SerializeCollectionDouble() + { + MakeCollectionTest(new Double[] { -0, 0, -10, 10, Double.MinValue, Double.MaxValue, Double.MinValue / 2, Double.MaxValue / 2 }); + } + + [TestMethod] + public void SerializeCollectionBoolean() + { + MakeCollectionTest(new Boolean[] { true, false, true }); + } + + [TestMethod] + public void SerializeCollectionByte() + { + MakeCollectionTest(new byte[] { 0, 3, -0, 1, 10, 128, 255 }); + } + + [TestMethod] + public void SerializeCollectionBytes() + { + var comparator = new Func((left, right) => + (left == null && (right == null || right.Length == 0)) || ArrayExtensions.UnsafeEquals(left, right)); + + MakeCollectionTest(new Byte[][] { null, new byte[] { }, new byte[] { 1 }, new byte[] { 0, 1, 10, 100, 128, 255 } }, comparator); + } + } +} diff --git a/ZeroLevel.UnitTests/ZeroLevel.UnitTests.csproj b/ZeroLevel.UnitTests/ZeroLevel.UnitTests.csproj new file mode 100644 index 0000000..12a3835 --- /dev/null +++ b/ZeroLevel.UnitTests/ZeroLevel.UnitTests.csproj @@ -0,0 +1,82 @@ + + + + + + Debug + AnyCPU + {C500B489-C432-499D-B0E4-B41998B12C49} + Library + Properties + ZeroLevel.UnitTests + ZeroLevel.UnitTests + v4.7.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + + + + + + + + + + {37020d8d-34e8-4ec3-a447-8396d5080457} + ZeroLevel + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/ZeroLevel.UnitTests/packages.config b/ZeroLevel.UnitTests/packages.config new file mode 100644 index 0000000..2f7c5a1 --- /dev/null +++ b/ZeroLevel.UnitTests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/ZeroLevel.sln b/ZeroLevel.sln index 279e1a3..e434381 100644 --- a/ZeroLevel.sln +++ b/ZeroLevel.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel", "ZeroLevel\Zero EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.Discovery", "ZeroLevel.Discovery\ZeroLevel.Discovery.csproj", "{4F55B23F-938C-4DA2-B6DC-B6BC66D36073}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZeroLevel.UnitTests", "ZeroLevel.UnitTests\ZeroLevel.UnitTests.csproj", "{C500B489-C432-499D-B0E4-B41998B12C49}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +43,18 @@ Global {4F55B23F-938C-4DA2-B6DC-B6BC66D36073}.Release|x64.Build.0 = Release|Any CPU {4F55B23F-938C-4DA2-B6DC-B6BC66D36073}.Release|x86.ActiveCfg = Release|Any CPU {4F55B23F-938C-4DA2-B6DC-B6BC66D36073}.Release|x86.Build.0 = Release|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Debug|x64.ActiveCfg = Debug|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Debug|x64.Build.0 = Debug|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Debug|x86.ActiveCfg = Debug|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Debug|x86.Build.0 = Debug|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Release|Any CPU.Build.0 = Release|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Release|x64.ActiveCfg = Release|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Release|x64.Build.0 = Release|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Release|x86.ActiveCfg = Release|Any CPU + {C500B489-C432-499D-B0E4-B41998B12C49}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ZeroLevel/Services/Extensions/BitConverterExtensions.cs b/ZeroLevel/Services/Extensions/BitConverterExtensions.cs index 43186ed..d1e0cd3 100644 --- a/ZeroLevel/Services/Extensions/BitConverterExtensions.cs +++ b/ZeroLevel/Services/Extensions/BitConverterExtensions.cs @@ -16,14 +16,11 @@ namespace ZeroLevel.Services.Extensions return bytes.ToArray(); } - public static decimal ToDecimal(this byte[] bytes) + public static decimal ToDecimal(this int[] parts) { - var arr = new int[4]; - for (int i = 0; i < 15; i += 4) - { - arr[i % 4] = BitConverter.ToInt32(bytes, i); - } - return new Decimal(arr); + bool sign = (parts[3] & 0x80000000) != 0; + byte scale = (byte)((parts[3] >> 16) & 0x7F); + return new Decimal(parts[0], parts[1], parts[2], sign, scale); } } } \ No newline at end of file diff --git a/ZeroLevel/Services/Network/NetUtils.cs b/ZeroLevel/Services/Network/NetUtils.cs index 32ab76a..a825a00 100644 --- a/ZeroLevel/Services/Network/NetUtils.cs +++ b/ZeroLevel/Services/Network/NetUtils.cs @@ -16,6 +16,13 @@ namespace ZeroLevel.Network return result == 0 ? x.Port.CompareTo(y.Port) : result; } + public static int Compare(this IPAddress x, IPAddress y) + { + var xx = x.ToString(); + var yy = y.ToString(); + return string.CompareOrdinal(xx, yy); + } + public static IPEndPoint CreateIPEndPoint(string endPoint) { string[] ep = endPoint.Split(':'); diff --git a/ZeroLevel/Services/Serialization/MemoryStreamReader.cs b/ZeroLevel/Services/Serialization/MemoryStreamReader.cs index 69c07e4..54b3f9e 100644 --- a/ZeroLevel/Services/Serialization/MemoryStreamReader.cs +++ b/ZeroLevel/Services/Serialization/MemoryStreamReader.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Net; using System.Text; +using ZeroLevel.Services.Extensions; namespace ZeroLevel.Services.Serialization { @@ -63,8 +64,11 @@ namespace ZeroLevel.Services.Serialization public decimal ReadDecimal() { - var buffer = ReadBuffer(4); - return BitConverter.ToInt32(buffer, 0); + var p1 = ReadInt32(); + var p2 = ReadInt32(); + var p3 = ReadInt32(); + var p4 = ReadInt32(); + return BitConverterExt.ToDecimal(new int[] { p1, p2, p3, p4 }); } /// @@ -143,13 +147,13 @@ namespace ZeroLevel.Services.Serialization public IPAddress ReadIP() { - var addr = ReadLong(); + var addr = ReadBytes(); return new IPAddress(addr); } public IPEndPoint ReadIPEndpoint() { - var addr = ReadLong(); + var addr = ReadIP(); var port = ReadInt32(); return new IPEndPoint(addr, port); } diff --git a/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs b/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs index 2379cd3..fd13764 100644 --- a/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs +++ b/ZeroLevel/Services/Serialization/MemoryStreamWriter.cs @@ -144,12 +144,12 @@ namespace ZeroLevel.Services.Serialization public void WriteIP(IPAddress ip) { - WriteLong(ip.Address); + WriteBytes(ip.GetAddressBytes()); } public void WriteIPEndpoint(IPEndPoint endpoint) { - WriteLong(endpoint.Address.Address); + WriteIP(endpoint.Address); WriteInt32(endpoint.Port); } diff --git a/ZeroLevel/obj/Debug/ZeroLevel.csproj.CoreCompileInputs.cache b/ZeroLevel/obj/Debug/ZeroLevel.csproj.CoreCompileInputs.cache index 1a69533..431e5cf 100644 --- a/ZeroLevel/obj/Debug/ZeroLevel.csproj.CoreCompileInputs.cache +++ b/ZeroLevel/obj/Debug/ZeroLevel.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -23b05e1da25edde99ed56a899f894605a2243d39 +4d221d21073e6ec70fc75d0c6e7d2458aa0f2b9a