From 27fc5ef9202d998365cbd58d32c7554b6a825177 Mon Sep 17 00:00:00 2001 From: Ygg01 Date: Sun, 7 Jan 2024 21:31:09 +0100 Subject: [PATCH] Add tests for Equality --- Linguini.Bundle.Test/Unit/BundleTests.cs | 35 +++++++++++++++++++ Linguini.Bundle/ConcurrentBundle.cs | 30 +++++++++++++--- Linguini.Bundle/FluentBundle.cs | 44 ++++++++++++++++++++++-- Linguini.Bundle/NonConcurrentBundle.cs | 26 +++++++++++--- 4 files changed, 124 insertions(+), 11 deletions(-) diff --git a/Linguini.Bundle.Test/Unit/BundleTests.cs b/Linguini.Bundle.Test/Unit/BundleTests.cs index 48f00d6..47e2179 100644 --- a/Linguini.Bundle.Test/Unit/BundleTests.cs +++ b/Linguini.Bundle.Test/Unit/BundleTests.cs @@ -358,5 +358,40 @@ public void TestDynamicSelectors() Assert.That(bundle.TryGetMessage("you-see", args, out _, out var message2)); Assert.That("You see a fairy.", Is.EqualTo(message2)); } + + [Test] + public void TestDeepClone() + { + var originalBundleOption = new FluentBundleOption + { + Locales = { "en-US" }, + MaxPlaceable = 123, + UseIsolating = false, + TransformFunc = _transform, + FormatterFunc = _formatter, + Functions = new Dictionary() + { + ["zero"] = _zeroFunc, + ["id"] = _idFunc, + } + }; + + // Assume FluentBundle object has DeepClone method + FluentBundle originalBundle = FluentBundle.MakeUnchecked(originalBundleOption); + FluentBundle clonedBundle = originalBundle.DeepClone(); + + // Assert that the original and cloned objects are not the same reference + Assert.That(originalBundle, Is.Not.SameAs(clonedBundle)); + + // Assert that the properties are copied properly + Assert.That(originalBundle, Is.EqualTo(clonedBundle)); + + // Assert that if original property is changed, new property isn't. + originalBundle.AddFunctionOverriding("zero", _idFunc); + clonedBundle.TryGetFunction("zero", out var clonedZero); + Assert.That((FluentFunction) _zeroFunc, Is.EqualTo(clonedZero)); + originalBundle.TryGetFunction("zero", out var originalZero); + Assert.That((FluentFunction) _idFunc, Is.EqualTo(originalZero)); + } } } \ No newline at end of file diff --git a/Linguini.Bundle/ConcurrentBundle.cs b/Linguini.Bundle/ConcurrentBundle.cs index fb7ab54..7126a06 100644 --- a/Linguini.Bundle/ConcurrentBundle.cs +++ b/Linguini.Bundle/ConcurrentBundle.cs @@ -13,7 +13,7 @@ namespace Linguini.Bundle { - public sealed class ConcurrentBundle : FluentBundle + public sealed class ConcurrentBundle : FluentBundle, IEquatable { internal ConcurrentDictionary FuncList = new(); private ConcurrentDictionary _terms = new(); @@ -49,7 +49,7 @@ protected override bool TryAddMessage(AstMessage message, List? err return false; } - + /// public override bool TryAddFunction(string funcName, ExternalFunction fluentFunction) { @@ -74,7 +74,7 @@ public override bool HasMessage(string identifier) { return _messages.ContainsKey(identifier); } - + /// public override bool TryGetAstMessage(string ident, [NotNullWhen(true)] out AstMessage? message) { @@ -111,7 +111,7 @@ public override IEnumerable GetTermEnumerable() { return _terms.Keys.ToArray(); } - + /// public override FluentBundle DeepClone() { @@ -120,7 +120,7 @@ public override FluentBundle DeepClone() FuncList = new ConcurrentDictionary(FuncList), _terms = new ConcurrentDictionary(_terms), _messages = new ConcurrentDictionary(_messages), - Culture = (CultureInfo) Culture.Clone(), + Culture = (CultureInfo)Culture.Clone(), Locales = new List(Locales), UseIsolating = UseIsolating, TransformFunc = (Func?)TransformFunc?.Clone(), @@ -129,6 +129,26 @@ public override FluentBundle DeepClone() EnableExtensions = EnableExtensions, }; } + + /// + public bool Equals(ConcurrentBundle? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && FuncList.SequenceEqual(other.FuncList) && _terms.SequenceEqual(other._terms) && + _messages.SequenceEqual(other._messages); + } + + /// + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || obj is ConcurrentBundle other && Equals(other); + } + /// + public override int GetHashCode() + { + return HashCode.Combine(base.GetHashCode(), FuncList, _terms, _messages); + } } } \ No newline at end of file diff --git a/Linguini.Bundle/FluentBundle.cs b/Linguini.Bundle/FluentBundle.cs index bf7530b..47ff25d 100644 --- a/Linguini.Bundle/FluentBundle.cs +++ b/Linguini.Bundle/FluentBundle.cs @@ -15,7 +15,7 @@ namespace Linguini.Bundle { - public abstract class FluentBundle + public abstract class FluentBundle : IEquatable { /// /// of the bundle. Primary bundle locale @@ -60,7 +60,7 @@ public abstract class FluentBundle /// // ReSharper disable once MemberCanBeProtected.Global public bool EnableExtensions { get; init; } - + public bool AddResource(string input, [NotNullWhen(false)] out List? errors) { var res = new LinguiniParser(input, EnableExtensions).Parse(); @@ -123,10 +123,23 @@ private void InternalResourceOverriding(Resource resource) } } + /// + /// Adds the given AstMessage to the collection of messages, by overriding any existing messages with the same name. + /// + /// The AstMessage to be added. protected abstract void AddMessageOverriding(AstMessage message); + /// + /// Adds a term to the AstTerm list, overriding any existing term with the same name. + /// + /// The term to be added. protected abstract void AddTermOverriding(AstTerm term); + /// + /// Adds a resource. + /// Any messages or terms in bundle will be overriden by the existing ones. + /// + /// The input string containing the resource data. public void AddResourceOverriding(string input) { var res = new LinguiniParser(input, EnableExtensions).Parse(); @@ -387,5 +400,32 @@ public static FluentBundle MakeUnchecked(FluentBundleOption option) } }; } + + /// + public bool Equals(FluentBundle? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Culture.Equals(other.Culture) && Locales.SequenceEqual(other.Locales) && + UseIsolating == other.UseIsolating && Equals(TransformFunc, other.TransformFunc) && + Equals(FormatterFunc, other.FormatterFunc) && MaxPlaceable == other.MaxPlaceable && + EnableExtensions == other.EnableExtensions; + } + + /// + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((FluentBundle)obj); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Culture, Locales, UseIsolating, TransformFunc, FormatterFunc, MaxPlaceable, + EnableExtensions); + } } } \ No newline at end of file diff --git a/Linguini.Bundle/NonConcurrentBundle.cs b/Linguini.Bundle/NonConcurrentBundle.cs index 72f553b..f8c7d2c 100644 --- a/Linguini.Bundle/NonConcurrentBundle.cs +++ b/Linguini.Bundle/NonConcurrentBundle.cs @@ -12,7 +12,7 @@ namespace Linguini.Bundle { - public sealed class NonConcurrentBundle : FluentBundle + public sealed class NonConcurrentBundle : FluentBundle, IEquatable { internal Dictionary FuncList = new(); private Dictionary _terms = new(); @@ -48,7 +48,7 @@ protected override bool TryAddMessage(AstMessage message, List? err return false; } - + /// public override bool TryAddFunction(string funcName, ExternalFunction fluentFunction) { @@ -72,7 +72,7 @@ public override bool HasMessage(string identifier) { return _messages.ContainsKey(identifier); } - + /// public override bool TryGetAstMessage(string ident, [NotNullWhen(true)] out AstMessage? message) { @@ -119,7 +119,7 @@ public override FluentBundle DeepClone() FuncList = new Dictionary(FuncList), _terms = new Dictionary(_terms), _messages = new Dictionary(_messages), - Culture = (CultureInfo) Culture.Clone(), + Culture = (CultureInfo)Culture.Clone(), Locales = new List(Locales), UseIsolating = UseIsolating, TransformFunc = (Func?)TransformFunc?.Clone(), @@ -128,5 +128,23 @@ public override FluentBundle DeepClone() EnableExtensions = EnableExtensions, }; } + + public bool Equals(NonConcurrentBundle? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && FuncList.SequenceEqual(other.FuncList) && _terms.SequenceEqual(other._terms) && + _messages.SequenceEqual(other._messages); + } + + public override bool Equals(object? obj) + { + return ReferenceEquals(this, obj) || obj is NonConcurrentBundle other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(base.GetHashCode(), FuncList, _terms, _messages); + } } } \ No newline at end of file