From af9f77c5556177942cb0a9d1ecf05b878b2f7f46 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Tue, 8 Feb 2022 23:24:57 +0100 Subject: [PATCH 01/12] Implement JsonUtility. Use JsonUtility for json conversion in API. --- .../ModuleEvents/ModuleEvents.OnNuiEvent.cs | 2 +- .../src/main/API/Nui/Bindings/NuiBind.cs | 16 ++---- NWN.Anvil/src/main/API/Nui/Layout/NuiGroup.cs | 3 +- NWN.Anvil/src/main/API/Object/NwPlayer.cs | 14 ++--- NWN.Anvil/src/main/API/Utils/JsonUtility.cs | 55 +++++++++++++++++++ .../API/Variable/Local/LocalVariableStruct.cs | 5 +- .../ObjectStorageVariableStruct.cs | 5 +- 7 files changed, 69 insertions(+), 31 deletions(-) create mode 100644 NWN.Anvil/src/main/API/Utils/JsonUtility.cs diff --git a/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs b/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs index f96a38268..69b8310fa 100644 --- a/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs +++ b/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs @@ -73,7 +73,7 @@ public OnNuiEvent() /// The payload data, or null if the event has no payload. public T GetEventPayload() { - return JsonConvert.DeserializeObject(eventPayload); + return JsonUtility.FromJson(eventPayload); } } } diff --git a/NWN.Anvil/src/main/API/Nui/Bindings/NuiBind.cs b/NWN.Anvil/src/main/API/Nui/Bindings/NuiBind.cs index 60eca11c4..26c986be6 100644 --- a/NWN.Anvil/src/main/API/Nui/Bindings/NuiBind.cs +++ b/NWN.Anvil/src/main/API/Nui/Bindings/NuiBind.cs @@ -27,8 +27,7 @@ public NuiBind(string key) /// The current value of the binding. public T GetBindValue(NwPlayer player, int uiToken) { - Json json = NWScript.NuiGetBind(player.ControlledCreature, uiToken, Key); - return JsonConvert.DeserializeObject(json.Dump()); + return JsonUtility.FromJson(NWScript.NuiGetBind(player.ControlledCreature, uiToken, Key)); } /// @@ -39,8 +38,7 @@ public T GetBindValue(NwPlayer player, int uiToken) /// The current values of the binding. public List GetBindValues(NwPlayer player, int uiToken) { - Json json = NWScript.NuiGetBind(player.ControlledCreature, uiToken, Key); - return JsonConvert.DeserializeObject>(json.Dump()); + return JsonUtility.FromJson>(NWScript.NuiGetBind(player.ControlledCreature, uiToken, Key)); } /// @@ -51,10 +49,7 @@ public List GetBindValues(NwPlayer player, int uiToken) /// The new value to assign. public void SetBindValue(NwPlayer player, int uiToken, T value) { - string jsonString = JsonConvert.SerializeObject(value); - Json json = Json.Parse(jsonString); - - NWScript.NuiSetBind(player.ControlledCreature, uiToken, Key, json); + NWScript.NuiSetBind(player.ControlledCreature, uiToken, Key, JsonUtility.ToJsonStructure(value)); } /// @@ -65,10 +60,7 @@ public void SetBindValue(NwPlayer player, int uiToken, T value) /// The new value to assign. public void SetBindValues(NwPlayer player, int uiToken, IEnumerable values) { - string jsonString = JsonConvert.SerializeObject(values); - Json json = Json.Parse(jsonString); - - NWScript.NuiSetBind(player.ControlledCreature, uiToken, Key, json); + NWScript.NuiSetBind(player.ControlledCreature, uiToken, Key, JsonUtility.ToJsonStructure(values)); } /// diff --git a/NWN.Anvil/src/main/API/Nui/Layout/NuiGroup.cs b/NWN.Anvil/src/main/API/Nui/Layout/NuiGroup.cs index e941a5821..7ac76df1b 100644 --- a/NWN.Anvil/src/main/API/Nui/Layout/NuiGroup.cs +++ b/NWN.Anvil/src/main/API/Nui/Layout/NuiGroup.cs @@ -38,8 +38,7 @@ public void SetLayout(NwPlayer player, int token, NuiLayout newLayout) throw new InvalidOperationException("Layout cannot be updated as the NuiGroup does not have an ID."); } - Json json = Json.Parse(JsonConvert.SerializeObject(newLayout)); - NWScript.NuiSetGroupLayout(player.ControlledCreature, token, Id, json); + NWScript.NuiSetGroupLayout(player.ControlledCreature, token, Id, JsonUtility.ToJsonStructure(newLayout)); } } } diff --git a/NWN.Anvil/src/main/API/Object/NwPlayer.cs b/NWN.Anvil/src/main/API/Object/NwPlayer.cs index c088d3be2..d0e5952c5 100644 --- a/NWN.Anvil/src/main/API/Object/NwPlayer.cs +++ b/NWN.Anvil/src/main/API/Object/NwPlayer.cs @@ -499,9 +499,7 @@ public void ClearTlkOverride(int strRef, bool restoreGlobal = true) /// The window token on success (!= 0), or 0 on error. public int CreateNuiWindow(NuiWindow window, string windowId = "") { - string jsonString = JsonConvert.SerializeObject(window); - Json json = Json.Parse(jsonString); - return NWScript.NuiCreate(ControlledCreature, json, windowId); + return NWScript.NuiCreate(ControlledCreature, JsonUtility.ToJsonStructure(window), windowId); } /// @@ -933,8 +931,7 @@ public void NuiDestroy(int uiToken) /// The fetched data, or null if the window does not exist on the given player, or has no userdata set. public T NuiGetUserData(int uiToken) { - Json json = NWScript.NuiGetUserData(ControlledCreature, uiToken); - return JsonConvert.DeserializeObject(json.Dump()); + return JsonUtility.FromJson(NWScript.NuiGetUserData(ControlledCreature, uiToken)); } /// @@ -958,8 +955,7 @@ public string NuiGetWindowId(int uiToken) /// The type of data to store. Must be serializable to JSON. public void NuiSetUserData(int uiToken, T userData) { - Json json = Json.Parse(JsonConvert.SerializeObject(userData)); - NWScript.NuiSetUserData(ControlledCreature, uiToken, json); + NWScript.NuiSetUserData(ControlledCreature, uiToken, JsonUtility.ToJsonStructure(userData)); } /// @@ -1293,9 +1289,7 @@ public async Task StoreCameraFacing() /// True if the window was successfully created, otherwise false. public bool TryCreateNuiWindow(NuiWindow window, out int token, string windowId = "") { - string jsonString = JsonConvert.SerializeObject(window); - Json json = Json.Parse(jsonString); - token = NWScript.NuiCreate(ControlledCreature, json, windowId); + token = NWScript.NuiCreate(ControlledCreature, JsonUtility.ToJsonStructure(window), windowId); return token != 0; } diff --git a/NWN.Anvil/src/main/API/Utils/JsonUtility.cs b/NWN.Anvil/src/main/API/Utils/JsonUtility.cs new file mode 100644 index 000000000..2628e10e6 --- /dev/null +++ b/NWN.Anvil/src/main/API/Utils/JsonUtility.cs @@ -0,0 +1,55 @@ +using Newtonsoft.Json; + +namespace Anvil.API +{ + /// + /// Utility methods for serializing/deserializing JSON data. + /// + public static class JsonUtility + { + /// + /// Deserializes a Json game engine structure. + /// + /// The json to deserialize. + /// The type to deserialize to. + /// The deserialized object. + internal static T FromJson(Json json) + { + return JsonConvert.DeserializeObject(json.Dump()); + } + + /// + /// Deserializes a JSON string. + /// + /// The JSON to deserialize. + /// The type to deserialize to. + /// The deserialized object. + public static T FromJson(string json) + { + return JsonConvert.DeserializeObject(json); + } + + /// + /// Serializes a value as JSON. + /// + /// The value to serialize. + /// The type of the value to serialize. + /// A JSON string representing the value. + public static string ToJson(T value) + { + return JsonConvert.SerializeObject(value); + } + + /// + /// Serializes a value as a JSON engine structure. + /// + /// The value to serialize. + /// The type of the value to serialize. + /// A JSON engine structure representing the value. + internal static Json ToJsonStructure(T value) + { + string serialized = ToJson(value); + return Json.Parse(serialized); + } + } +} diff --git a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableStruct.cs b/NWN.Anvil/src/main/API/Variable/Local/LocalVariableStruct.cs index 303ba526f..23b7e51ad 100644 --- a/NWN.Anvil/src/main/API/Variable/Local/LocalVariableStruct.cs +++ b/NWN.Anvil/src/main/API/Variable/Local/LocalVariableStruct.cs @@ -1,4 +1,3 @@ -using System.Text.Json; using NWN.Core; namespace Anvil.API @@ -15,8 +14,8 @@ public sealed class LocalVariableStruct : LocalVariable { public override T Value { - get => HasValue ? JsonSerializer.Deserialize(((Json)NWScript.GetLocalJson(Object, Name)).Dump()) : default; - set => NWScript.SetLocalJson(Object, Name, Json.Parse(JsonSerializer.Serialize(value))); + get => HasValue ? JsonUtility.FromJson(NWScript.GetLocalJson(Object, Name)) : default; + set => NWScript.SetLocalJson(Object, Name, JsonUtility.ToJsonStructure(value)); } public override void Delete() diff --git a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableStruct.cs b/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableStruct.cs index 349983388..6983d04b5 100644 --- a/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableStruct.cs +++ b/NWN.Anvil/src/main/API/Variable/ObjectStorage/ObjectStorageVariableStruct.cs @@ -1,4 +1,3 @@ -using System.Text.Json; using Anvil.Services; namespace Anvil.API @@ -12,8 +11,8 @@ public abstract class ObjectStorageVariableStruct : ObjectStorageVariable public sealed override T Value { - get => HasValue ? JsonSerializer.Deserialize(ObjectStorageService.GetObjectStorage(Object).GetString(ObjectStoragePrefix, Key)) : default; - set => ObjectStorageService.GetObjectStorage(Object).Set(ObjectStoragePrefix, Key, JsonSerializer.Serialize(value), Persist); + get => HasValue ? JsonUtility.FromJson(ObjectStorageService.GetObjectStorage(Object).GetString(ObjectStoragePrefix, Key)) : default; + set => ObjectStorageService.GetObjectStorage(Object).Set(ObjectStoragePrefix, Key, JsonUtility.ToJson(value), Persist); } protected sealed override string VariableTypePrefix => "PERSTR!"; From 6337012084eca44f00059a1104b347ac206613e3 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Tue, 8 Feb 2022 23:25:11 +0100 Subject: [PATCH 02/12] Add JsonUtility tests. --- .../NWN.Anvil.Tests.csproj.DotSettings | 2 + .../src/main/API/Utils/JsonUtilityTests.cs | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 NWN.Anvil.Tests/src/main/API/Utils/JsonUtilityTests.cs diff --git a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings index f99e4ea18..0ece3b52b 100644 --- a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings +++ b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings @@ -2,8 +2,10 @@ True True True + True True True True + True True True \ No newline at end of file diff --git a/NWN.Anvil.Tests/src/main/API/Utils/JsonUtilityTests.cs b/NWN.Anvil.Tests/src/main/API/Utils/JsonUtilityTests.cs new file mode 100644 index 000000000..f45d4030b --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Utils/JsonUtilityTests.cs @@ -0,0 +1,92 @@ +using Anvil.API; +using NUnit.Framework; + +// ReSharper disable UnusedAutoPropertyAccessor.Local +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Utils")] + public sealed class JsonUtilityTests + { + [Test(Description = "Serializing a value creates valid json.")] + [TestCase(null, "null")] + [TestCase(1, "1")] + [TestCase(1f, "1.0")] + [TestCase(1.532f, "1.532")] + [TestCase(1.0d, "1.0")] + [TestCase(1.689d, "1.689")] + [TestCase(false, "false")] + [TestCase(true, "true")] + [TestCase("test", @"""test""")] + [TestCase("", @"""""")] + public void SerializeValueCreatesValidJson(object value, string expected) + { + Assert.That(JsonUtility.ToJson(value), Is.EqualTo(expected)); + } + + [Test(Description = "Serializing a struct creates valid json.")] + public void SerializeStructCreatesValidJson() + { + TestStruct value = new TestStruct + { + TestB = true, + TestF = 10f, + TestI = 5, + TestS = "test", + }; + + Assert.That(JsonUtility.ToJson(value), Is.EqualTo(@"{""TestI"":5,""TestS"":""test"",""TestF"":10.0,""TestB"":true}")); + } + + [Test(Description = "Serializing a class creates valid json.")] + public void SerializeClassCreatesValidJson() + { + TestClass value = new TestClass + { + TestB = true, + TestF = 10f, + TestI = 5, + TestS = "test", + }; + + Assert.That(JsonUtility.ToJson(value), Is.EqualTo(@"{""TestI"":5,""TestS"":""test"",""TestF"":10.0,""TestB"":true}")); + } + + [Test(Description = "Serializing a record creates valid json.")] + public void SerializeRecordCreatesValidJson() + { + TestRecord value = new TestRecord + { + TestB = true, + TestF = 10f, + TestI = 5, + TestS = "test", + }; + + Assert.That(JsonUtility.ToJson(value), Is.EqualTo(@"{""TestI"":5,""TestS"":""test"",""TestF"":10.0,""TestB"":true}")); + } + + private struct TestStruct + { + public int TestI { get; set; } + public string TestS { get; set; } + public float TestF { get; set; } + public bool TestB { get; set; } + } + + private sealed class TestClass + { + public int TestI { get; set; } + public string TestS { get; set; } + public float TestF { get; set; } + public bool TestB { get; set; } + } + + private sealed record TestRecord + { + public int TestI { get; set; } + public string TestS { get; set; } + public float TestF { get; set; } + public bool TestB { get; set; } + } + } +} From cca27fa62761eafda88f95aae79b9134f6073ab9 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Wed, 9 Feb 2022 00:20:22 +0100 Subject: [PATCH 03/12] Add NuiBinding tests. Fix NuiValue deserialization. --- .../src/main/API/Nui/NuiBindingTests.cs | 210 ++++++++++++++++++ .../src/main/API/Nui/Bindings/NuiValue.cs | 2 - .../API/Nui/Bindings/NuiValueConverter.cs | 6 +- 3 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 NWN.Anvil.Tests/src/main/API/Nui/NuiBindingTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Nui/NuiBindingTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/NuiBindingTests.cs new file mode 100644 index 000000000..0a1c3736c --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Nui/NuiBindingTests.cs @@ -0,0 +1,210 @@ +using System.Collections.Generic; +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Nui")] + public sealed class NuiBindingTests + { + [Test(Description = "Serializing a NuiBind creates a valid JSON structure.")] + public void SerializeNuiBindStringReturnsValidJsonStructure() + { + NuiBind test = new NuiBind("test"); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"{""bind"":""test""}")); + } + + [Test(Description = "Serializing a NuiBind creates a valid JSON structure.")] + public void SerializeNuiBindNuiRectReturnsValidJsonStructure() + { + NuiBind test = new NuiBind("test"); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"{""bind"":""test""}")); + } + + [Test(Description = "Serializing a NuiValue creates a valid JSON structure.")] + [TestCase("test", @"""test""")] + [TestCase(null, @"null")] + [TestCase("", @"""""")] + public void SerializeNuiValueStringReturnsValidJsonStructure(string value, string expected) + { + NuiValue test = new NuiValue(value); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(expected)); + } + + [Test(Description = "Serializing a NuiValue creates a valid JSON structure.")] + [TestCase(0, @"0")] + [TestCase(-0, @"0")] + [TestCase(10, @"10")] + [TestCase(-10, @"-10")] + [TestCase(int.MaxValue, @"2147483647")] + [TestCase(int.MinValue, @"-2147483648")] + public void SerializeNuiValueIntReturnsValidJsonStructure(int value, string expected) + { + NuiValue test = new NuiValue(value); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(expected)); + } + + [Test(Description = "Serializing a NuiValue creates a valid JSON structure.")] + [TestCase(0, @"0")] + [TestCase(-0, @"0")] + [TestCase(10, @"10")] + [TestCase(-10, @"-10")] + [TestCase(null, @"null")] + [TestCase(int.MaxValue, @"2147483647")] + [TestCase(int.MinValue, @"-2147483648")] + public void SerializeNuiValueNullableIntReturnsValidJsonStructure(int? value, string expected) + { + NuiValue test = new NuiValue(value); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(expected)); + } + + [Test(Description = "Serializing a NuiValue creates a valid JSON structure.")] + [TestCase(0f, @"0.0")] + [TestCase(0.1f, @"0.1")] + [TestCase(0.125f, @"0.125")] + [TestCase(2f, @"2.0")] + [TestCase(2.5f, @"2.5")] + [TestCase(2.5122f, @"2.5122")] + [TestCase(float.NaN, @"""NaN""")] + [TestCase(float.NegativeInfinity, @"""-Infinity""")] + [TestCase(float.PositiveInfinity, @"""Infinity""")] + public void SerializeNuiValueFloatReturnsValidJsonStructure(float value, string expected) + { + NuiValue test = new NuiValue(value); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(expected)); + } + + [Test(Description = "Serializing a NuiValue creates a valid JSON structure.")] + [TestCase(0f, @"0.0")] + [TestCase(0.1f, @"0.1")] + [TestCase(0.125f, @"0.125")] + [TestCase(2f, @"2.0")] + [TestCase(2.5f, @"2.5")] + [TestCase(2.5122f, @"2.5122")] + [TestCase(null, @"null")] + [TestCase(float.NaN, @"""NaN""")] + [TestCase(float.NegativeInfinity, @"""-Infinity""")] + [TestCase(float.PositiveInfinity, @"""Infinity""")] + public void SerializeNuiValueFloatNullableReturnsValidJsonStructure(float? value, string expected) + { + NuiValue test = new NuiValue(value); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(expected)); + } + + [Test(Description = "Serializing a NuiValue creates a valid JSON structure.")] + public void SerializeNuiValueNuiRectReturnsValidJsonStructure() + { + NuiValue test = new NuiValue(new NuiRect(100f, 50.251f, 30.11f, 20f)); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"{""h"":20.0,""w"":30.11,""x"":100.0,""y"":50.251}")); + } + + [Test(Description = "Serializing a NuiValue> creates a valid JSON structure.")] + public void SerializeNuiValueIntListReturnsValidJsonStructure() + { + NuiValue> test = new NuiValue>(new List { 1, 2, 3 }); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"[1,2,3]")); + } + + [Test(Description = "Deerializing a NuiBind creates a valid JSON structure.")] + public void DeserializeNuiBindStringReturnsValidJsonStructure() + { + NuiBind test = JsonUtility.FromJson>(@"{""bind"":""test""}"); + Assert.That(test.Key, Is.EqualTo("test")); + } + + [Test(Description = "Deerializing a NuiBind creates a valid JSON structure.")] + public void DeserializeNuiBindNuiRectReturnsValidJsonStructure() + { + NuiBind test = JsonUtility.FromJson>(@"{""bind"":""test""}"); + Assert.That(test.Key, Is.EqualTo("test")); + } + + [Test(Description = "Deerializing a NuiValue creates a valid JSON structure.")] + [TestCase("test", @"""test""")] + [TestCase(null, @"null")] + [TestCase("", @"""""")] + public void DeserializeNuiValueStringReturnsValidJsonStructure(string expected, string serialized) + { + NuiValue test = JsonUtility.FromJson>(serialized); + Assert.That(test.Value, Is.EqualTo(expected)); + } + + [Test(Description = "Deerializing a NuiValue creates a valid JSON structure.")] + [TestCase(0, @"0")] + [TestCase(-0, @"0")] + [TestCase(10, @"10")] + [TestCase(-10, @"-10")] + [TestCase(int.MaxValue, @"2147483647")] + [TestCase(int.MinValue, @"-2147483648")] + public void DeserializeNuiValueIntReturnsValidJsonStructure(int expected, string serialized) + { + NuiValue test = JsonUtility.FromJson>(serialized); + Assert.That(test.Value, Is.EqualTo(expected)); + } + + [Test(Description = "Deerializing a NuiValue creates a valid JSON structure.")] + [TestCase(0, @"0")] + [TestCase(-0, @"0")] + [TestCase(10, @"10")] + [TestCase(-10, @"-10")] + [TestCase(null, @"null")] + [TestCase(int.MaxValue, @"2147483647")] + [TestCase(int.MinValue, @"-2147483648")] + public void DeserializeNuiValueNullableIntReturnsValidJsonStructure(int? expected, string serialized) + { + NuiValue test = JsonUtility.FromJson>(serialized); + Assert.That(test.Value, Is.EqualTo(expected)); + } + + [Test(Description = "Deerializing a NuiValue creates a valid JSON structure.")] + [TestCase(0f, @"0.0")] + [TestCase(0.1f, @"0.1")] + [TestCase(0.125f, @"0.125")] + [TestCase(2f, @"2.0")] + [TestCase(2.5f, @"2.5")] + [TestCase(2.5122f, @"2.5122")] + [TestCase(float.NaN, @"""NaN""")] + [TestCase(float.NegativeInfinity, @"""-Infinity""")] + [TestCase(float.PositiveInfinity, @"""Infinity""")] + public void DeserializeNuiValueFloatReturnsValidJsonStructure(float expected, string serialized) + { + NuiValue test = JsonUtility.FromJson>(serialized); + Assert.That(test.Value, Is.EqualTo(expected)); + } + + [Test(Description = "Deerializing a NuiValue creates a valid JSON structure.")] + [TestCase(0f, @"0.0")] + [TestCase(0.1f, @"0.1")] + [TestCase(0.125f, @"0.125")] + [TestCase(2f, @"2.0")] + [TestCase(2.5f, @"2.5")] + [TestCase(2.5122f, @"2.5122")] + [TestCase(null, @"null")] + [TestCase(float.NaN, @"""NaN""")] + [TestCase(float.NegativeInfinity, @"""-Infinity""")] + [TestCase(float.PositiveInfinity, @"""Infinity""")] + public void DeserializeNuiValueFloatNullableReturnsValidJsonStructure(float? expected, string serialized) + { + NuiValue test = JsonUtility.FromJson>(serialized); + Assert.That(test.Value, Is.EqualTo(expected)); + } + + [Test(Description = "Deerializing a NuiValue creates a valid JSON structure.")] + public void DeserializeNuiValueNuiRectReturnsValidJsonStructure() + { + NuiValue test = JsonUtility.FromJson>(@"{""h"":20.0,""w"":30.11,""x"":100.0,""y"":50.251}"); + NuiRect expected = new NuiRect(100.0f, 50.251f, 30.11f, 20.0f); + + Assert.That(test.Value, Is.EqualTo(expected)); + } + + [Test(Description = "Deserializing a NuiValue> creates a valid JSON structure.")] + public void DeserializeNuiValueIntListReturnsValidJsonStructure() + { + NuiValue> test = JsonUtility.FromJson>>(@"[1,2,3]"); + List expected = new List { 1, 2, 3 }; + + Assert.That(test.Value, Is.EqualTo(expected)); + } + } +} diff --git a/NWN.Anvil/src/main/API/Nui/Bindings/NuiValue.cs b/NWN.Anvil/src/main/API/Nui/Bindings/NuiValue.cs index d37138a90..2d98de587 100644 --- a/NWN.Anvil/src/main/API/Nui/Bindings/NuiValue.cs +++ b/NWN.Anvil/src/main/API/Nui/Bindings/NuiValue.cs @@ -1,4 +1,3 @@ -using JetBrains.Annotations; using Newtonsoft.Json; namespace Anvil.API @@ -15,7 +14,6 @@ public NuiValue(T value) Value = value; } - [UsedImplicitly] internal NuiValue() {} /// diff --git a/NWN.Anvil/src/main/API/Nui/Bindings/NuiValueConverter.cs b/NWN.Anvil/src/main/API/Nui/Bindings/NuiValueConverter.cs index c7e8fe33e..d7091a22a 100644 --- a/NWN.Anvil/src/main/API/Nui/Bindings/NuiValueConverter.cs +++ b/NWN.Anvil/src/main/API/Nui/Bindings/NuiValueConverter.cs @@ -13,7 +13,7 @@ public override bool CanConvert(Type objectType) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - object retVal = Activator.CreateInstance(objectType); + object retVal = Activator.CreateInstance(objectType, true); if (retVal == null) { return null; @@ -25,7 +25,9 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist return null; } - propertyInfo.SetValue(retVal, serializer.Deserialize(reader)); + Type valueType = objectType.GetGenericArguments()[0]; + propertyInfo.SetValue(retVal, serializer.Deserialize(reader, valueType)); + return retVal; } From 62ed40b806c978d6aab8f0d9b1ae6409ea6cbdce Mon Sep 17 00:00:00 2001 From: Jorteck Date: Wed, 9 Feb 2022 00:27:32 +0100 Subject: [PATCH 04/12] Cleanup usings. --- .../main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs | 1 - NWN.Anvil/src/main/API/Object/NwPlayer.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs b/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs index 69b8310fa..881d1b858 100644 --- a/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs +++ b/NWN.Anvil/src/main/API/Events/Game/ModuleEvents/ModuleEvents.OnNuiEvent.cs @@ -1,6 +1,5 @@ using System; using Anvil.API.Events; -using Newtonsoft.Json; using NWN.Core; namespace Anvil.API.Events diff --git a/NWN.Anvil/src/main/API/Object/NwPlayer.cs b/NWN.Anvil/src/main/API/Object/NwPlayer.cs index d0e5952c5..2a445b246 100644 --- a/NWN.Anvil/src/main/API/Object/NwPlayer.cs +++ b/NWN.Anvil/src/main/API/Object/NwPlayer.cs @@ -7,7 +7,6 @@ using Anvil.API.Events; using Anvil.Internal; using Anvil.Services; -using Newtonsoft.Json; using NLog; using NWN.Core; using NWN.Native.API; From 6364c52ef370c7c7b51112c9609d675f4add22c4 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Thu, 10 Feb 2022 00:23:18 +0100 Subject: [PATCH 05/12] Add NuiLayout tests. --- .../src/main/API/Nui/NuiLayoutTests.cs | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs new file mode 100644 index 000000000..0db21d9d1 --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Nui")] + public sealed class NuiLayoutTests + { + [Test(Description = "Serializing a NuiGroup creates a valid JSON structure.")] + public void SerializeNuiGroupReturnsValidJsonStructure() + { + NuiGroup nuiGroup = new NuiGroup + { + Id = "test_group", + Aspect = 1.5f, + Border = true, + Enabled = new NuiBind("enabled_bind"), + Height = 10f, + Margin = 2f, + Padding = 3f, + ForegroundColor = new NuiBind("color_bind"), + Scrollbars = NuiScrollbars.Both, + Tooltip = "test_tooltip", + Width = 100f, + Visible = false, + Layout = new NuiColumn + { + Children = new List + { + new NuiLabel("Test"), + }, + }, + }; + + Assert.That(JsonUtility.ToJson(nuiGroup), Is.EqualTo(@"{""border"":true,""scrollbars"":3,""type"":""group"",""children"":[{""type"":""col"",""children"":[{""text_halign"":1,""value"":""Test"",""type"":""label"",""text_valign"":1}]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_group"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); + } + + [Test(Description = "Serializing a NuiColumn creates a valid JSON structure.")] + public void SerializeNuiColumnReturnsValidJsonStructure() + { + NuiColumn nuiColumn = new NuiColumn + { + Id = "test_column", + Aspect = 1.5f, + Enabled = new NuiBind("enabled_bind"), + Height = 10f, + Margin = 2f, + Padding = 3f, + ForegroundColor = new NuiBind("color_bind"), + Tooltip = "test_tooltip", + Width = 100f, + Visible = false, + Children = new List + { + new NuiLabel("test"), + new NuiRow(), + }, + }; + + Assert.That(JsonUtility.ToJson(nuiColumn), Is.EqualTo(@"{""type"":""col"",""children"":[{""text_halign"":1,""value"":""test"",""type"":""label"",""text_valign"":1},{""type"":""row"",""children"":[]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_column"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); + } + + [Test(Description = "Serializing a NuiRow creates a valid JSON structure.")] + public void SerializeNuiRowReturnsValidJsonStructure() + { + NuiRow nuiRow = new NuiRow + { + Id = "test_row", + Aspect = 1.5f, + Enabled = new NuiBind("enabled_bind"), + Height = 10f, + Margin = 2f, + Padding = 3f, + ForegroundColor = new NuiBind("color_bind"), + Tooltip = "test_tooltip", + Width = 100f, + Visible = false, + Children = new List + { + new NuiLabel("test"), + new NuiRow(), + }, + }; + + Assert.That(JsonUtility.ToJson(nuiRow), Is.EqualTo(@"{""type"":""row"",""children"":[{""text_halign"":1,""value"":""test"",""type"":""label"",""text_valign"":1},{""type"":""row"",""children"":[]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_row"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); + } + } +} From 5bc7269837e0225a9b6da499a177d4bda27fd20d Mon Sep 17 00:00:00 2001 From: Jorteck Date: Fri, 25 Feb 2022 21:48:55 +0100 Subject: [PATCH 06/12] Add closure tests. --- .../src/main/API/Object/NwObjectTests.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 NWN.Anvil.Tests/src/main/API/Object/NwObjectTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Object/NwObjectTests.cs b/NWN.Anvil.Tests/src/main/API/Object/NwObjectTests.cs new file mode 100644 index 000000000..bc581be1f --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Object/NwObjectTests.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Anvil.API; +using Anvil.Tests.Resources; +using NUnit.Framework; +using NWN.Core; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Object")] + public sealed class NwObjectTests + { + private readonly List createdTestObjects = new List(); + + [Test(Description = "Tests if assigning an action/closure using WaitForObjectContext correctly updates the script context.")] + [Timeout(5000)] + public async Task WaitForObjectContextEntersCorrectContext() + { + NwModule module = NwModule.Instance; + + await module.WaitForObjectContext(); + + Assert.That(NWScript.OBJECT_SELF.ToNwObject(), Is.EqualTo(module)); + + NwCreature creature = NwCreature.Create(StandardResRef.Creature.nw_bandit001, NwModule.Instance.StartingLocation); + createdTestObjects.Add(creature); + + await creature.WaitForObjectContext(); + + Assert.That(NWScript.OBJECT_SELF.ToNwObject(), Is.EqualTo(creature)); + } + + [Test(Description = "Tests if adding an action correctly queues an action on the game object.")] + [Timeout(5000)] + public async Task QueueCreatureActionIsQueued() + { + NwCreature creature = NwCreature.Create(StandardResRef.Creature.nw_bandit001, NwModule.Instance.StartingLocation); + createdTestObjects.Add(creature); + + bool actionExecuted = false; + await creature.AddActionToQueue(() => + { + actionExecuted = true; + }); + + await NwTask.WaitUntil(() => actionExecuted); + Assert.That(actionExecuted, Is.EqualTo(true)); + } + + [TearDown] + public void CleanupTestObjects() + { + foreach (NwGameObject testObject in createdTestObjects) + { + testObject.PlotFlag = false; + testObject.Destroy(); + } + + createdTestObjects.Clear(); + } + } +} From 3966819da59145545a61203a0d9e75493af44616 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Fri, 25 Feb 2022 22:20:10 +0100 Subject: [PATCH 07/12] Move tests to subdirectories/sub classes. --- .../NWN.Anvil.Tests.csproj.DotSettings | 2 + .../src/main/API/Nui/Bindings/NuiBindTests.cs | 37 ++++++++ .../NuiValueTests.cs} | 30 +------ .../src/main/API/Nui/Layout/NuiColumnTests.cs | 35 ++++++++ .../src/main/API/Nui/Layout/NuiGroupTests.cs | 39 ++++++++ .../src/main/API/Nui/Layout/NuiRowTests.cs | 35 ++++++++ .../src/main/API/Nui/NuiLayoutTests.cs | 89 ------------------- 7 files changed, 149 insertions(+), 118 deletions(-) create mode 100644 NWN.Anvil.Tests/src/main/API/Nui/Bindings/NuiBindTests.cs rename NWN.Anvil.Tests/src/main/API/Nui/{NuiBindingTests.cs => Bindings/NuiValueTests.cs} (85%) create mode 100644 NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiColumnTests.cs create mode 100644 NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiGroupTests.cs create mode 100644 NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiRowTests.cs delete mode 100644 NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs diff --git a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings index 0ece3b52b..795ec635c 100644 --- a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings +++ b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings @@ -3,6 +3,8 @@ True True True + True + True True True True diff --git a/NWN.Anvil.Tests/src/main/API/Nui/Bindings/NuiBindTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/Bindings/NuiBindTests.cs new file mode 100644 index 000000000..484341079 --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Nui/Bindings/NuiBindTests.cs @@ -0,0 +1,37 @@ +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Nui")] + public sealed class NuiBindTests + { + [Test(Description = "Serializing a NuiBind creates a valid JSON structure.")] + public void SerializeNuiBindStringReturnsValidJsonStructure() + { + NuiBind test = new NuiBind("test"); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"{""bind"":""test""}")); + } + + [Test(Description = "Serializing a NuiBind creates a valid JSON structure.")] + public void SerializeNuiBindNuiRectReturnsValidJsonStructure() + { + NuiBind test = new NuiBind("test"); + Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"{""bind"":""test""}")); + } + + [Test(Description = "Deerializing a NuiBind creates a valid JSON structure.")] + public void DeserializeNuiBindStringReturnsValidJsonStructure() + { + NuiBind test = JsonUtility.FromJson>(@"{""bind"":""test""}"); + Assert.That(test.Key, Is.EqualTo("test")); + } + + [Test(Description = "Deerializing a NuiBind creates a valid JSON structure.")] + public void DeserializeNuiBindNuiRectReturnsValidJsonStructure() + { + NuiBind test = JsonUtility.FromJson>(@"{""bind"":""test""}"); + Assert.That(test.Key, Is.EqualTo("test")); + } + } +} diff --git a/NWN.Anvil.Tests/src/main/API/Nui/NuiBindingTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/Bindings/NuiValueTests.cs similarity index 85% rename from NWN.Anvil.Tests/src/main/API/Nui/NuiBindingTests.cs rename to NWN.Anvil.Tests/src/main/API/Nui/Bindings/NuiValueTests.cs index 0a1c3736c..de9e63323 100644 --- a/NWN.Anvil.Tests/src/main/API/Nui/NuiBindingTests.cs +++ b/NWN.Anvil.Tests/src/main/API/Nui/Bindings/NuiValueTests.cs @@ -5,22 +5,8 @@ namespace Anvil.Tests.API { [TestFixture(Category = "API.Nui")] - public sealed class NuiBindingTests + public class NuiValueTests { - [Test(Description = "Serializing a NuiBind creates a valid JSON structure.")] - public void SerializeNuiBindStringReturnsValidJsonStructure() - { - NuiBind test = new NuiBind("test"); - Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"{""bind"":""test""}")); - } - - [Test(Description = "Serializing a NuiBind creates a valid JSON structure.")] - public void SerializeNuiBindNuiRectReturnsValidJsonStructure() - { - NuiBind test = new NuiBind("test"); - Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"{""bind"":""test""}")); - } - [Test(Description = "Serializing a NuiValue creates a valid JSON structure.")] [TestCase("test", @"""test""")] [TestCase(null, @"null")] @@ -105,20 +91,6 @@ public void SerializeNuiValueIntListReturnsValidJsonStructure() Assert.That(JsonUtility.ToJson(test), Is.EqualTo(@"[1,2,3]")); } - [Test(Description = "Deerializing a NuiBind creates a valid JSON structure.")] - public void DeserializeNuiBindStringReturnsValidJsonStructure() - { - NuiBind test = JsonUtility.FromJson>(@"{""bind"":""test""}"); - Assert.That(test.Key, Is.EqualTo("test")); - } - - [Test(Description = "Deerializing a NuiBind creates a valid JSON structure.")] - public void DeserializeNuiBindNuiRectReturnsValidJsonStructure() - { - NuiBind test = JsonUtility.FromJson>(@"{""bind"":""test""}"); - Assert.That(test.Key, Is.EqualTo("test")); - } - [Test(Description = "Deerializing a NuiValue creates a valid JSON structure.")] [TestCase("test", @"""test""")] [TestCase(null, @"null")] diff --git a/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiColumnTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiColumnTests.cs new file mode 100644 index 000000000..92da3238b --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiColumnTests.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Nui")] + public sealed class NuiColumnTests + { + [Test(Description = "Serializing a NuiColumn creates a valid JSON structure.")] + public void SerializeNuiColumnReturnsValidJsonStructure() + { + NuiColumn nuiColumn = new NuiColumn + { + Id = "test_column", + Aspect = 1.5f, + Enabled = new NuiBind("enabled_bind"), + Height = 10f, + Margin = 2f, + Padding = 3f, + ForegroundColor = new NuiBind("color_bind"), + Tooltip = "test_tooltip", + Width = 100f, + Visible = false, + Children = new List + { + new NuiLabel("test"), + new NuiRow(), + }, + }; + + Assert.That(JsonUtility.ToJson(nuiColumn), Is.EqualTo(@"{""type"":""col"",""children"":[{""text_halign"":1,""value"":""test"",""type"":""label"",""text_valign"":1},{""type"":""row"",""children"":[]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_column"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); + } + } +} diff --git a/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiGroupTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiGroupTests.cs new file mode 100644 index 000000000..fdbad518b --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiGroupTests.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Nui")] + public sealed class NuiGroupTests + { + [Test(Description = "Serializing a NuiGroup creates a valid JSON structure.")] + public void SerializeNuiGroupReturnsValidJsonStructure() + { + NuiGroup nuiGroup = new NuiGroup + { + Id = "test_group", + Aspect = 1.5f, + Border = true, + Enabled = new NuiBind("enabled_bind"), + Height = 10f, + Margin = 2f, + Padding = 3f, + ForegroundColor = new NuiBind("color_bind"), + Scrollbars = NuiScrollbars.Both, + Tooltip = "test_tooltip", + Width = 100f, + Visible = false, + Layout = new NuiColumn + { + Children = new List + { + new NuiLabel("Test"), + }, + }, + }; + + Assert.That(JsonUtility.ToJson(nuiGroup), Is.EqualTo(@"{""border"":true,""scrollbars"":3,""type"":""group"",""children"":[{""type"":""col"",""children"":[{""text_halign"":1,""value"":""Test"",""type"":""label"",""text_valign"":1}]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_group"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); + } + } +} diff --git a/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiRowTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiRowTests.cs new file mode 100644 index 000000000..9c7939fbe --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Nui/Layout/NuiRowTests.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Nui")] + public sealed class NuiRowTests + { + [Test(Description = "Serializing a NuiRow creates a valid JSON structure.")] + public void SerializeNuiRowReturnsValidJsonStructure() + { + NuiRow nuiRow = new NuiRow + { + Id = "test_row", + Aspect = 1.5f, + Enabled = new NuiBind("enabled_bind"), + Height = 10f, + Margin = 2f, + Padding = 3f, + ForegroundColor = new NuiBind("color_bind"), + Tooltip = "test_tooltip", + Width = 100f, + Visible = false, + Children = new List + { + new NuiLabel("test"), + new NuiRow(), + }, + }; + + Assert.That(JsonUtility.ToJson(nuiRow), Is.EqualTo(@"{""type"":""row"",""children"":[{""text_halign"":1,""value"":""test"",""type"":""label"",""text_valign"":1},{""type"":""row"",""children"":[]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_row"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); + } + } +} diff --git a/NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs deleted file mode 100644 index 0db21d9d1..000000000 --- a/NWN.Anvil.Tests/src/main/API/Nui/NuiLayoutTests.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Collections.Generic; -using Anvil.API; -using NUnit.Framework; - -namespace Anvil.Tests.API -{ - [TestFixture(Category = "API.Nui")] - public sealed class NuiLayoutTests - { - [Test(Description = "Serializing a NuiGroup creates a valid JSON structure.")] - public void SerializeNuiGroupReturnsValidJsonStructure() - { - NuiGroup nuiGroup = new NuiGroup - { - Id = "test_group", - Aspect = 1.5f, - Border = true, - Enabled = new NuiBind("enabled_bind"), - Height = 10f, - Margin = 2f, - Padding = 3f, - ForegroundColor = new NuiBind("color_bind"), - Scrollbars = NuiScrollbars.Both, - Tooltip = "test_tooltip", - Width = 100f, - Visible = false, - Layout = new NuiColumn - { - Children = new List - { - new NuiLabel("Test"), - }, - }, - }; - - Assert.That(JsonUtility.ToJson(nuiGroup), Is.EqualTo(@"{""border"":true,""scrollbars"":3,""type"":""group"",""children"":[{""type"":""col"",""children"":[{""text_halign"":1,""value"":""Test"",""type"":""label"",""text_valign"":1}]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_group"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); - } - - [Test(Description = "Serializing a NuiColumn creates a valid JSON structure.")] - public void SerializeNuiColumnReturnsValidJsonStructure() - { - NuiColumn nuiColumn = new NuiColumn - { - Id = "test_column", - Aspect = 1.5f, - Enabled = new NuiBind("enabled_bind"), - Height = 10f, - Margin = 2f, - Padding = 3f, - ForegroundColor = new NuiBind("color_bind"), - Tooltip = "test_tooltip", - Width = 100f, - Visible = false, - Children = new List - { - new NuiLabel("test"), - new NuiRow(), - }, - }; - - Assert.That(JsonUtility.ToJson(nuiColumn), Is.EqualTo(@"{""type"":""col"",""children"":[{""text_halign"":1,""value"":""test"",""type"":""label"",""text_valign"":1},{""type"":""row"",""children"":[]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_column"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); - } - - [Test(Description = "Serializing a NuiRow creates a valid JSON structure.")] - public void SerializeNuiRowReturnsValidJsonStructure() - { - NuiRow nuiRow = new NuiRow - { - Id = "test_row", - Aspect = 1.5f, - Enabled = new NuiBind("enabled_bind"), - Height = 10f, - Margin = 2f, - Padding = 3f, - ForegroundColor = new NuiBind("color_bind"), - Tooltip = "test_tooltip", - Width = 100f, - Visible = false, - Children = new List - { - new NuiLabel("test"), - new NuiRow(), - }, - }; - - Assert.That(JsonUtility.ToJson(nuiRow), Is.EqualTo(@"{""type"":""row"",""children"":[{""text_halign"":1,""value"":""test"",""type"":""label"",""text_valign"":1},{""type"":""row"",""children"":[]}],""aspect"":1.5,""enabled"":{""bind"":""enabled_bind""},""foreground_color"":{""bind"":""color_bind""},""height"":10.0,""id"":""test_row"",""margin"":2.0,""padding"":3.0,""tooltip"":""test_tooltip"",""visible"":false,""width"":100.0}")); - } - } -} From 292041d49756266de8334a0a8de171ea4bd7752f Mon Sep 17 00:00:00 2001 From: Jorteck Date: Sat, 26 Feb 2022 02:29:10 +0100 Subject: [PATCH 08/12] Ignore unused json fields for Nui Colors. --- NWN.Anvil/src/main/API/Color.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NWN.Anvil/src/main/API/Color.cs b/NWN.Anvil/src/main/API/Color.cs index 5eb2a7717..04330a556 100644 --- a/NWN.Anvil/src/main/API/Color.cs +++ b/NWN.Anvil/src/main/API/Color.cs @@ -67,21 +67,25 @@ public Color(float red, float green, float blue, float alpha = 1.0f) /// /// Gets the alpha value of this color as a float (0-1). /// + [JsonIgnore] public float AlphaF => Alpha / 255f; /// /// Gets the blue value of this color as a float (0-1). /// + [JsonIgnore] public float BlueF => Blue / 255f; /// /// Gets the green value of this color as a float (0-1). /// + [JsonIgnore] public float GreenF => Green / 255f; /// /// Gets the red value of this color as a float (0-1). /// + [JsonIgnore] public float RedF => Red / 255f; /// From f16079d7cce255406407575809b0cc621fb81f02 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Sat, 26 Feb 2022 02:30:43 +0100 Subject: [PATCH 09/12] Add NwTask.Delay tests. --- .../src/main/API/Async/NwTaskTests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/NWN.Anvil.Tests/src/main/API/Async/NwTaskTests.cs b/NWN.Anvil.Tests/src/main/API/Async/NwTaskTests.cs index 5b3e694c2..f48d15c43 100644 --- a/NWN.Anvil.Tests/src/main/API/Async/NwTaskTests.cs +++ b/NWN.Anvil.Tests/src/main/API/Async/NwTaskTests.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading.Tasks; using Anvil.API; using Anvil.Services; @@ -13,6 +14,7 @@ public sealed class NwTaskTests private static VirtualMachine VirtualMachine { get; set; } [Test(Description = "Starts an async task, then attempts to switch back to the main thread & script context.")] + [Timeout(10000)] public async Task ReturnToMainThreadAfterSwitch() { await Task.Run(async () => @@ -24,5 +26,21 @@ await Task.Run(async () => await NwTask.SwitchToMainThread(); Assert.That(VirtualMachine.IsInScriptContext, Is.True, "Did not return to the main thread after SwitchToMainThread."); } + + [Test(Description = "Await a fixed delay then continue execution.")] + [Timeout(10000)] + [TestCase(500)] + [TestCase(1000)] + [TestCase(5000)] + public async Task AwaitDelayContinuesExecutionAtExpectedTime(int delayMs) + { + TimeSpan delay = TimeSpan.FromMilliseconds(delayMs); + Stopwatch stopwatch = Stopwatch.StartNew(); + + await NwTask.Delay(delay); + + Assert.That(stopwatch.Elapsed.TotalMilliseconds, Is.EqualTo(delay.TotalMilliseconds).Within(2).Percent, "Delay was not within the margin of error."); + Assert.That(VirtualMachine.IsInScriptContext, Is.True, "Did not return to the main thread after NwTask.Delay."); + } } } From 52a494a6f8801ebc271177a6a78b95d366f87fb1 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Sat, 26 Feb 2022 02:31:04 +0100 Subject: [PATCH 10/12] Add scheduler service tests. --- .../Scheduler/SchedulerServiceTests.cs | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 NWN.Anvil.Tests/src/main/Services/Scheduler/SchedulerServiceTests.cs diff --git a/NWN.Anvil.Tests/src/main/Services/Scheduler/SchedulerServiceTests.cs b/NWN.Anvil.Tests/src/main/Services/Scheduler/SchedulerServiceTests.cs new file mode 100644 index 000000000..3bf65846c --- /dev/null +++ b/NWN.Anvil.Tests/src/main/Services/Scheduler/SchedulerServiceTests.cs @@ -0,0 +1,102 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Anvil.API; +using Anvil.Services; +using NUnit.Framework; + +namespace Anvil.Tests.Services +{ + [TestFixture(Category = "Services.Scheduler")] + public sealed class SchedulerServiceTests + { + [Inject] + private static SchedulerService SchedulerService { get; set; } + + [Test(Description = "Scheduling a task correctly schedules and runs the task with the specified delay.")] + [Timeout(10000)] + [TestCase(500)] + [TestCase(1000)] + [TestCase(5000)] + public async Task ScheduleDelayRunsTaskAfterDelay(int delayMs) + { + TimeSpan delay = TimeSpan.FromMilliseconds(delayMs); + Stopwatch stopwatch = Stopwatch.StartNew(); + + bool executed = false; + SchedulerService.Schedule(() => + { + Assert.That(stopwatch.Elapsed.TotalMilliseconds, Is.EqualTo(delay.TotalMilliseconds).Within(2).Percent, "Delay was not within the margin of error."); + executed = true; + }, delay); + + await NwTask.WaitUntil(() => executed); + Assert.That(executed, Is.EqualTo(true)); + } + + [Test(Description = "Scheduling a task and cancelling it prevents the schedule from running.")] + [Timeout(10000)] + [TestCase(500)] + [TestCase(1000)] + [TestCase(5000)] + public async Task ScheduleAndCancelDoesNotRunTask(int delayMs) + { + TimeSpan delay = TimeSpan.FromMilliseconds(delayMs); + + ScheduledTask task = SchedulerService.Schedule(() => + { + Assert.Fail("The task executed when it shouldn't have."); + }, delay); + + task.Cancel(); + await NwTask.Delay(delay + TimeSpan.FromSeconds(1)); + } + + [Test(Description = "Scheduling a repeating task correctly schedules and runs the task with the specified interval.")] + [Timeout(10000)] + [TestCase(500)] + [TestCase(1000)] + public async Task RepeatingScheduleRunsRepeatingTask(int delayMs) + { + TimeSpan interval = TimeSpan.FromMilliseconds(delayMs); + Stopwatch stopwatch = Stopwatch.StartNew(); + + int executionCount = 0; + ScheduledTask task = SchedulerService.ScheduleRepeating(() => + { + Assert.That(stopwatch.Elapsed.TotalMilliseconds, Is.EqualTo(interval.TotalMilliseconds).Within(2).Percent, "Delay was not within the margin of error."); + executionCount++; + stopwatch.Restart(); + }, interval); + + await NwTask.WaitUntil(() => executionCount > 5); + Assert.That(executionCount, Is.GreaterThan(5)); + + task.Cancel(); + } + + [Test(Description = "Scheduling a repeating task and cancelling the task after the first run correctly cancels the task.")] + [Timeout(10000)] + [TestCase(500)] + [TestCase(1000)] + public async Task RepeatingScheduleCancelAfterRunPreventsSubsequentRuns(int delayMs) + { + TimeSpan interval = TimeSpan.FromMilliseconds(delayMs); + + ScheduledTask scheduledTask = null; + int executionCount = 0; + + scheduledTask = SchedulerService.ScheduleRepeating(() => + { + Assert.That(executionCount, Is.EqualTo(0), "Repeating task ran after being cancelled."); + executionCount++; + + // ReSharper disable once AccessToModifiedClosure + scheduledTask!.Cancel(); + }, interval); + + await NwTask.WaitUntil(() => executionCount > 0); + Assert.That(executionCount, Is.EqualTo(1)); + } + } +} From 868f3b9417e03860a2c29ab2700bda942d2b9c56 Mon Sep 17 00:00:00 2001 From: Jorteck Date: Sat, 26 Feb 2022 02:31:16 +0100 Subject: [PATCH 11/12] Add NuiDrawList tests. --- .../main/API/Nui/Widgets/NuiDrawListTests.cs | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 NWN.Anvil.Tests/src/main/API/Nui/Widgets/NuiDrawListTests.cs diff --git a/NWN.Anvil.Tests/src/main/API/Nui/Widgets/NuiDrawListTests.cs b/NWN.Anvil.Tests/src/main/API/Nui/Widgets/NuiDrawListTests.cs new file mode 100644 index 000000000..f3022eda8 --- /dev/null +++ b/NWN.Anvil.Tests/src/main/API/Nui/Widgets/NuiDrawListTests.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using Anvil.API; +using NUnit.Framework; + +namespace Anvil.Tests.API +{ + [TestFixture(Category = "API.Nui")] + public sealed class NuiDrawListTests + { + [Test(Description = "Serializing a NuiDrawListArc creates a valid JSON structure.")] + public void SerializeNuiDrawListArcReturnsValidJsonStructure() + { + NuiDrawListArc drawListArc = new NuiDrawListArc(ColorConstants.Pink, true, 1.0f, new NuiVector(1.0f, 2.0f), 2.0f, 90f, 170f) + { + Enabled = false, + }; + + Assert.That(JsonUtility.ToJson(drawListArc), Is.EqualTo(@"{""amax"":170.0,""amin"":90.0,""c"":{""x"":1.0,""y"":2.0},""radius"":2.0,""type"":3,""color"":{""a"":255,""b"":170,""g"":170,""r"":255},""enabled"":false,""fill"":true,""line_thickness"":1.0}")); + } + + [Test(Description = "Serializing a NuiDrawListCircle creates a valid JSON structure.")] + public void SerializeNuiDrawListCircleReturnsValidJsonStructure() + { + NuiDrawListCircle drawListCircle = new NuiDrawListCircle(ColorConstants.Pink, true, 1.0f, new NuiRect(1.0f, 2.0f, 3.0f, 4.0f)) + { + Enabled = false, + }; + + Assert.That(JsonUtility.ToJson(drawListCircle), Is.EqualTo(@"{""rect"":{""h"":4.0,""w"":3.0,""x"":1.0,""y"":2.0},""type"":2,""color"":{""a"":255,""b"":170,""g"":170,""r"":255},""enabled"":false,""fill"":true,""line_thickness"":1.0}")); + } + + [Test(Description = "Serializing a NuiDrawListCurve creates a valid JSON structure.")] + public void SerializeNuiDrawListCurveReturnsValidJsonStructure() + { + NuiDrawListCurve drawListCurve = new NuiDrawListCurve(ColorConstants.Pink, 1.0f, new NuiVector(10.0f, 5.0f), new NuiVector(6.0f, 2.0f), new NuiVector(9.5f, 3.0f), new NuiVector(22.0f, 11.3f)) + { + Enabled = false, + }; + + Assert.That(JsonUtility.ToJson(drawListCurve), Is.EqualTo(@"{""ctrl0"":{""x"":9.5,""y"":3.0},""ctrl1"":{""x"":22.0,""y"":11.3},""a"":{""x"":10.0,""y"":5.0},""b"":{""x"":6.0,""y"":2.0},""type"":1,""color"":{""a"":255,""b"":170,""g"":170,""r"":255},""enabled"":false,""fill"":false,""line_thickness"":1.0}")); + } + + [Test(Description = "Serializing a NuiDrawListImage creates a valid JSON structure.")] + public void SerializeNuiDrawListImageReturnsValidJsonStructure() + { + NuiDrawListImage drawListImage = new NuiDrawListImage("test_img", new NuiRect(1.0f, 2.0f, 3.0f, 4.0f)) + { + Enabled = false, + }; + + Assert.That(JsonUtility.ToJson(drawListImage), Is.EqualTo(@"{""image_aspect"":3,""image_halign"":1,""rect"":{""h"":4.0,""w"":3.0,""x"":1.0,""y"":2.0},""image"":""test_img"",""type"":5,""image_valign"":1,""color"":null,""enabled"":false,""fill"":null,""line_thickness"":null}")); + } + + [Test(Description = "Serializing a NuiDrawListPolyLine creates a valid JSON structure.")] + public void SerializeNuiDrawListPolyLineReturnsValidJsonStructure() + { + NuiDrawListPolyLine drawListPolyLine = new NuiDrawListPolyLine(ColorConstants.Pink, true, 2.0f, new List { 2.0f, 4.0f, 6.0f, 11.0f }) + { + Enabled = false, + }; + + Assert.That(JsonUtility.ToJson(drawListPolyLine), Is.EqualTo(@"{""points"":[2.0,4.0,6.0,11.0],""type"":0,""color"":{""a"":255,""b"":170,""g"":170,""r"":255},""enabled"":false,""fill"":true,""line_thickness"":2.0}")); + } + + [Test(Description = "Serializing a NuiDrawListText creates a valid JSON structure.")] + public void SerializeNuiDrawListTextReturnsValidJsonStructure() + { + NuiDrawListText drawListText = new NuiDrawListText(ColorConstants.Pink, new NuiRect(5.0f, 6.0f, 7.0f, 8.0f), "Test string") + { + Enabled = false, + }; + + Assert.That(JsonUtility.ToJson(drawListText), Is.EqualTo(@"{""rect"":{""h"":8.0,""w"":7.0,""x"":5.0,""y"":6.0},""text"":""Test string"",""type"":4,""color"":{""a"":255,""b"":170,""g"":170,""r"":255},""enabled"":false,""fill"":null,""line_thickness"":null}")); + } + } +} From f163614e8540e9ccb7682acb93f1458bc26cbcfd Mon Sep 17 00:00:00 2001 From: Jorteck Date: Sat, 26 Feb 2022 02:31:23 +0100 Subject: [PATCH 12/12] Update namespace ignores. --- NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings index 795ec635c..de4dd1d16 100644 --- a/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings +++ b/NWN.Anvil.Tests/NWN.Anvil.Tests.csproj.DotSettings @@ -5,9 +5,11 @@ True True True + True True True True True True - True \ No newline at end of file + True + True \ No newline at end of file