From f58f3bf78ce1136dd6a785ef1182aa09227e9ddf Mon Sep 17 00:00:00 2001 From: Ygg01 Date: Sun, 21 Jan 2024 21:48:21 +0100 Subject: [PATCH] Refactor and fix serializers for FunctionReference and DynamicReference. The serializers for FunctionReference and DynamicReference have been refactored and fixed. This includes updating conditions for properties and handling potential null values. Removed redundant converter in TestUtil following restructuring of serialization tests. --- .../AttributeSerializerTest.cs | 56 ------------ .../CallArgumentsSerializerTest.cs | 71 --------------- .../DynamicReferenceSerializerTest.cs | 84 ------------------ .../SerializeAndDeserializeTest.cs | 86 +++++++++++++++++++ Linguini.Serialization.Test/TestUtil.cs | 35 ++------ .../Converters/DynamicReferenceSerializer.cs | 10 +-- .../Converters/FunctionReferenceSerializer.cs | 15 ++-- .../Parser/LinguiniTestDetailedErrors.cs | 2 - Linguini.Syntax/Ast/Expression.cs | 16 +--- PluralRules.Test/Cldr/CldrParserTest.cs | 4 +- 10 files changed, 108 insertions(+), 271 deletions(-) delete mode 100644 Linguini.Serialization.Test/AttributeSerializerTest.cs delete mode 100644 Linguini.Serialization.Test/CallArgumentsSerializerTest.cs delete mode 100644 Linguini.Serialization.Test/DynamicReferenceSerializerTest.cs create mode 100644 Linguini.Serialization.Test/SerializeAndDeserializeTest.cs diff --git a/Linguini.Serialization.Test/AttributeSerializerTest.cs b/Linguini.Serialization.Test/AttributeSerializerTest.cs deleted file mode 100644 index a19774b..0000000 --- a/Linguini.Serialization.Test/AttributeSerializerTest.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Text; -using System.Text.Json; -using Linguini.Serialization.Converters; -using Linguini.Syntax.Ast; -using NUnit.Framework; -using Attribute = Linguini.Syntax.Ast.Attribute; - -namespace Linguini.Serialization.Test; - -[TestFixture] -public class AttributeSerializerTest -{ - [Test] - [TestOf(typeof(AttributeSerializer))] - [Parallelizable] - public void TestAttributeSerializer() - { - string attributeJson = @" -{ - ""type"": ""Attribute"", - ""id"": { - ""type"": ""Identifier"", - ""name"": ""desc"" - }, - ""value"": { - ""type"": ""Pattern"", - ""elements"": [ - { - ""type"": ""StringLiteral"", - ""value"": ""description"" - } - ] - } -}"; - Attribute expected = new Attribute("desc", new PatternBuilder("description")); - Attribute? actual = JsonSerializer.Deserialize(attributeJson, TestUtil.Options); - Assert.That(actual, Is.EqualTo(expected)); - } - - [Test] - [TestOf(typeof(AttributeSerializer))] - [Parallelizable] - public void Serde() - { - Attribute start = new Attribute("desc", new PatternBuilder("d1")); - var text = ""; - using (var memoryStream = new MemoryStream()) - { - JsonSerializer.Serialize(memoryStream, start, TestUtil.Options); - text = Encoding.UTF8.GetString(memoryStream.ToArray()); - } - - Attribute deserialized = JsonSerializer.Deserialize(text, TestUtil.Options)!; - Assert.That(deserialized, Is.EqualTo(start)); - } -} \ No newline at end of file diff --git a/Linguini.Serialization.Test/CallArgumentsSerializerTest.cs b/Linguini.Serialization.Test/CallArgumentsSerializerTest.cs deleted file mode 100644 index 40441b6..0000000 --- a/Linguini.Serialization.Test/CallArgumentsSerializerTest.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Text; -using System.Text.Json; -using Linguini.Serialization.Converters; -using Linguini.Syntax.Ast; -using NUnit.Framework; - -namespace Linguini.Serialization.Test; - -[TestFixture] -public class CallArgumentsSerializerTest -{ - [Test] - [TestOf(typeof(CallArgumentsSerializer))] - [Parallelizable] - public void TestCallSerializer() - { - string callJson = @" -{ - ""type"": ""CallArguments"", - ""positional"": [ - { - ""type"": ""MessageReference"", - ""id"": { - ""type"": ""Identifier"", - ""name"": ""x"" - }, - ""attribute"": null - } - ], - ""named"": [ - { - ""type"": ""NamedArgument"", - ""name"": { - ""type"": ""Identifier"", - ""name"": ""y"" - }, - ""value"": { - ""value"": ""3"", - ""type"": ""NumberLiteral"" - } - } - ] -}"; - var expected = new CallArgumentsBuilder() - .AddPositionalArg(InlineExpressionBuilder.CreateMessageReference("x")) - .AddNamedArg("y", 3) - .Build(); - CallArguments? actual = JsonSerializer.Deserialize(callJson, TestUtil.Options); - Assert.That(actual, Is.EqualTo(expected)); - } - - [Test] - [TestOf(typeof(CallArgumentsSerializer))] - [Parallelizable] - public void Serde() - { - var start = new CallArgumentsBuilder() - .AddNamedArg("x", 3) - .AddPositionalArg(InlineExpressionBuilder.CreateTermReference("x", "y")) - .Build(); - var text = ""; - using (var memoryStream = new MemoryStream()) - { - JsonSerializer.Serialize(memoryStream, start, TestUtil.Options); - text = Encoding.UTF8.GetString(memoryStream.ToArray()); - } - - CallArguments deserialized = JsonSerializer.Deserialize(text, TestUtil.Options); - Assert.That(deserialized, Is.EqualTo(start)); - } -} \ No newline at end of file diff --git a/Linguini.Serialization.Test/DynamicReferenceSerializerTest.cs b/Linguini.Serialization.Test/DynamicReferenceSerializerTest.cs deleted file mode 100644 index ad8c191..0000000 --- a/Linguini.Serialization.Test/DynamicReferenceSerializerTest.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Text; -using System.Text.Json; -using Linguini.Serialization.Converters; -using Linguini.Syntax.Ast; -using NUnit.Framework; - -namespace Linguini.Serialization.Test; - -[TestFixture] -public class DynamicReferenceSerializerTest -{ - [Test] - [TestOf(typeof(DynamicReferenceSerializer))] - [Parallelizable] - public void TestDynamicReference() - { - string dynamicReference = @" -{ - ""type"": ""DynamicReference"", - ""id"": { - ""type"": ""Identifier"", - ""name"": ""dyn"" - }, - ""attribute"": { - ""type"": ""Identifier"", - ""name"": ""attr"" - }, - ""arguments"": { - ""type"": ""CallArguments"", - ""positional"": [ - { - ""type"": ""MessageReference"", - ""id"": { - ""type"": ""Identifier"", - ""name"": ""x"" - }, - ""attribute"": null - } - ], - ""named"": [ - { - ""type"": ""NamedArgument"", - ""name"": { - ""type"": ""Identifier"", - ""name"": ""y"" - }, - ""value"": { - ""value"": ""3"", - ""type"": ""NumberLiteral"" - } - } - ] - } -}"; - var callArgs = new CallArgumentsBuilder() - .AddPositionalArg(InlineExpressionBuilder.CreateMessageReference("x")) - .AddNamedArg("y", 3); - var expected = new DynamicReference("dyn", "attr", callArgs); - DynamicReference? actual = JsonSerializer.Deserialize(dynamicReference, TestUtil.Options); - Assert.That(actual, Is.EqualTo(expected)); - } - - [Test] - [TestOf(typeof(DynamicReferenceSerializer))] - [Parallelizable] - public void Serde() - { - var callArgs = new CallArgumentsBuilder() - .AddPositionalArg(InlineExpressionBuilder.CreateMessageReference("x")) - .AddNamedArg("y", 3) - .AddPositionalArg("z") - .AddNamedArg("om", InlineExpressionBuilder.CreateMessageReference("a", "z")); - var start = new DynamicReference("dyn", "attr", callArgs); - var text = ""; - using (var memoryStream = new MemoryStream()) - { - JsonSerializer.Serialize(memoryStream, start, TestUtil.Options); - text = Encoding.UTF8.GetString(memoryStream.ToArray()); - } - - DynamicReference deserialized = JsonSerializer.Deserialize(text, TestUtil.Options)!; - Assert.That(deserialized, Is.EqualTo(start)); - } -} \ No newline at end of file diff --git a/Linguini.Serialization.Test/SerializeAndDeserializeTest.cs b/Linguini.Serialization.Test/SerializeAndDeserializeTest.cs new file mode 100644 index 0000000..7f18b67 --- /dev/null +++ b/Linguini.Serialization.Test/SerializeAndDeserializeTest.cs @@ -0,0 +1,86 @@ +using System.Diagnostics; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Json.Serialization; +using Linguini.Serialization.Converters; +using Linguini.Syntax.Ast; +using NUnit.Framework; +using Attribute = Linguini.Syntax.Ast.Attribute; + + +namespace Linguini.Serialization.Test; + +[TestFixture] +public class SerializeAndDeserializeTest +{ + [Test] + [TestCaseSource(nameof(AstExamples))] + [Parallelizable] + public void SerializeDeserializeTest(object x) + { + SerializeAndDeserializeTest.SerializeDeserializeTest(x); + } + + public static IEnumerable AstExamples() + { + yield return new CallArgumentsBuilder() + .AddPositionalArg(InlineExpressionBuilder.CreateMessageReference("x")) + .AddNamedArg("y", 3) + .Build(); + yield return new Attribute("desc", new PatternBuilder("description")); + yield return new DynamicReference("dyn", "attr", new CallArgumentsBuilder() + .AddPositionalArg(InlineExpressionBuilder.CreateMessageReference("x")) + .AddNamedArg("y", 3)); + yield return new FunctionReference("foo", new CallArgumentsBuilder() + .AddPositionalArg(3) + .AddNamedArg("test", InlineExpressionBuilder.CreateTermReference("x", "y")) + .Build() + ); + yield return new Identifier("test"); + } + + private static void SerializeDeserializeTest(T expected) + { + // Serialize the object to JSON string. + var jsonString = JsonSerializer.Serialize(expected, Options); + + // Deserialize the JSON string back into an object. + Debug.Assert(expected != null, nameof(expected) + " != null"); + var deserializedObject = JsonSerializer.Deserialize(jsonString, expected.GetType(), Options); + + // Now you have a 'deserializedObject' which should be equivalent to the original 'expected' object. + Assert.That(deserializedObject, Is.Not.Null); + Assert.That(deserializedObject, Is.EqualTo(expected)); + } + + private static readonly JsonSerializerOptions Options = new() + { + IgnoreReadOnlyFields = false, + WriteIndented = true, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + Converters = + { + new AttributeSerializer(), + new CallArgumentsSerializer(), + new CommentSerializer(), + new FunctionReferenceSerializer(), + new IdentifierSerializer(), + new JunkSerializer(), + new MessageReferenceSerializer(), + new MessageSerializer(), + new DynamicReferenceSerializer(), + new NamedArgumentSerializer(), + new ParseErrorSerializer(), + new PatternSerializer(), + new PlaceableSerializer(), + new ResourceSerializer(), + new PlaceableSerializer(), + new SelectExpressionSerializer(), + new TermReferenceSerializer(), + new TermSerializer(), + new VariantSerializer(), + new VariableReferenceSerializer(), + } + }; +} \ No newline at end of file diff --git a/Linguini.Serialization.Test/TestUtil.cs b/Linguini.Serialization.Test/TestUtil.cs index 41455da..35c2f15 100644 --- a/Linguini.Serialization.Test/TestUtil.cs +++ b/Linguini.Serialization.Test/TestUtil.cs @@ -2,40 +2,15 @@ using System.Text.Json; using System.Text.Json.Serialization; using Linguini.Serialization.Converters; +using NUnit.Framework; namespace Linguini.Serialization.Test { public static class TestUtil { - public static readonly JsonSerializerOptions Options = new() - { - IgnoreReadOnlyFields = false, - WriteIndented = true, - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - Converters = - { - new AttributeSerializer(), - new CallArgumentsSerializer(), - new CommentSerializer(), - new FunctionReferenceSerializer(), - new IdentifierSerializer(), - new JunkSerializer(), - new MessageReferenceSerializer(), - new MessageSerializer(), - new DynamicReferenceSerializer(), - new NamedArgumentSerializer(), - new ParseErrorSerializer(), - new PatternSerializer(), - new PlaceableSerializer(), - new ResourceSerializer(), - new PlaceableSerializer(), - new SelectExpressionSerializer(), - new TermReferenceSerializer(), - new TermSerializer(), - new VariantSerializer(), - new VariableReferenceSerializer(), - } - }; + + + + } } \ No newline at end of file diff --git a/Linguini.Serialization/Converters/DynamicReferenceSerializer.cs b/Linguini.Serialization/Converters/DynamicReferenceSerializer.cs index f54ca72..d7d4198 100644 --- a/Linguini.Serialization/Converters/DynamicReferenceSerializer.cs +++ b/Linguini.Serialization/Converters/DynamicReferenceSerializer.cs @@ -11,7 +11,8 @@ public class DynamicReferenceSerializer : JsonConverter public override DynamicReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - throw new NotImplementedException(); + var el = JsonSerializer.Deserialize(ref reader, options); + return ProcessDynamicReference(el, options); } public override void Write(Utf8JsonWriter writer, DynamicReference dynRef, JsonSerializerOptions options) @@ -40,9 +41,8 @@ public override void Write(Utf8JsonWriter writer, DynamicReference dynRef, JsonS public static DynamicReference ProcessDynamicReference(JsonElement el, JsonSerializerOptions options) { - Identifier? identifier = null; - if (!el.TryGetProperty("id", out var jsonId) && - !IdentifierSerializer.TryGetIdentifier(jsonId, options, out identifier)) + if (!el.TryGetProperty("id", out var jsonId) || + !IdentifierSerializer.TryGetIdentifier(jsonId, options, out var identifier)) { throw new JsonException("Dynamic reference must contain at least `id` field"); } @@ -59,7 +59,7 @@ public static DynamicReference ProcessDynamicReference(JsonElement el, CallArgumentsSerializer.TryGetCallArguments(jsonArgs, options, out arguments); } - return new DynamicReference(identifier!, attribute, arguments); + return new DynamicReference(identifier, attribute, arguments); } } } \ No newline at end of file diff --git a/Linguini.Serialization/Converters/FunctionReferenceSerializer.cs b/Linguini.Serialization/Converters/FunctionReferenceSerializer.cs index 639a671..5e7f7c7 100644 --- a/Linguini.Serialization/Converters/FunctionReferenceSerializer.cs +++ b/Linguini.Serialization/Converters/FunctionReferenceSerializer.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using Linguini.Syntax.Ast; @@ -11,7 +10,8 @@ public class FunctionReferenceSerializer : JsonConverter public override FunctionReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - throw new NotImplementedException(); + var el = JsonSerializer.Deserialize(ref reader, options); + return ProcessFunctionReference(el, options); } public override void Write(Utf8JsonWriter writer, FunctionReference value, JsonSerializerOptions options) @@ -29,23 +29,22 @@ public override void Write(Utf8JsonWriter writer, FunctionReference value, JsonS public static FunctionReference ProcessFunctionReference(JsonElement el, JsonSerializerOptions options) { - Identifier? ident = null; - if (!el.TryGetProperty("id", out JsonElement value) && - !IdentifierSerializer.TryGetIdentifier(value, options, out ident)) + if (!el.TryGetProperty("id", out JsonElement value) || + !IdentifierSerializer.TryGetIdentifier(value, options, out var ident)) { throw new JsonException("Function reference must contain `id` field"); } CallArguments? arguments = null; - if (!el.TryGetProperty("arguments", out var jsonArguments) && - CallArgumentsSerializer.TryGetCallArguments(jsonArguments, options, out arguments) + if (!el.TryGetProperty("arguments", out var jsonArguments) || + !CallArgumentsSerializer.TryGetCallArguments(jsonArguments, options, out arguments) ) { throw new JsonException("Function reference must contain `arguments` field"); } - return new FunctionReference(ident!, arguments!.Value); + return new FunctionReference(ident, arguments.Value); } } } \ No newline at end of file diff --git a/Linguini.Syntax.Tests/Parser/LinguiniTestDetailedErrors.cs b/Linguini.Syntax.Tests/Parser/LinguiniTestDetailedErrors.cs index d437bb7..d7581fd 100644 --- a/Linguini.Syntax.Tests/Parser/LinguiniTestDetailedErrors.cs +++ b/Linguini.Syntax.Tests/Parser/LinguiniTestDetailedErrors.cs @@ -1,8 +1,6 @@ using System; -using Linguini.Syntax.Ast; using Linguini.Syntax.Parser; using NUnit.Framework; -using NUnit.Framework.Legacy; namespace Linguini.Syntax.Tests.Parser { diff --git a/Linguini.Syntax/Ast/Expression.cs b/Linguini.Syntax/Ast/Expression.cs index 3b25f2f..3fe907c 100644 --- a/Linguini.Syntax/Ast/Expression.cs +++ b/Linguini.Syntax/Ast/Expression.cs @@ -155,14 +155,14 @@ public bool Equals(MessageReference? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Id.Equals(other.Id) && Equals(Attribute, other.Attribute); + return Id.Equals(other.Id) && Identifier.Comparator.Equals(Attribute, other.Attribute); } public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; + if (obj.GetType() != GetType()) return false; return Equals((MessageReference)obj); } @@ -217,7 +217,7 @@ public bool Equals(DynamicReference? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return Id.Equals(other.Id) && Equals(Attribute, other.Attribute) && + return Id.Equals(other.Id) && Identifier.Comparator.Equals(Attribute, other.Attribute) && Nullable.Equals(Arguments, other.Arguments); } @@ -342,16 +342,6 @@ public Placeable(IExpression expression) Expression = expression; } - public bool Equals(IPatternElement? other) - { - if (other is Placeable otherPlaceable) - { - return Expression == otherPlaceable.Expression; - } - - return false; - } - public bool Equals(Placeable? other) { if (ReferenceEquals(null, other)) return false; diff --git a/PluralRules.Test/Cldr/CldrParserTest.cs b/PluralRules.Test/Cldr/CldrParserTest.cs index 3b3c28b..7339254 100644 --- a/PluralRules.Test/Cldr/CldrParserTest.cs +++ b/PluralRules.Test/Cldr/CldrParserTest.cs @@ -60,8 +60,8 @@ public void ParseEmpty() [Parallelizable] [TestCase("n is 12 @integer 0, 5, 7~20", new[] {"0", "5", "7~20"}, new string[] { })] [TestCase("n is 12 @integer 0, 5, 7~20 @decimal 1, 3~6,...", new[] {"0", "5", "7~20"}, - new string[] {"1", "3~6"})] - [TestCase("@integer 0, 11~25, 100, 1000, …", new string[] {"0", "11~25", "100", "1000"}, new string[] { })] + new[] {"1", "3~6"})] + [TestCase("@integer 0, 11~25, 100, 1000, …", new[] {"0", "11~25", "100", "1000"}, new string[] { })] public void ParseSamples(string input, string[] expIntRangeList, string[] expDecRangeList) { var rule = new CldrParser(input).ParseRule();