From 8fb192632dfa32d9c0e8367f8681c24233ea3ed7 Mon Sep 17 00:00:00 2001 From: Jonas Nyrup Date: Sun, 31 Oct 2021 08:42:43 +0100 Subject: [PATCH] Add test of clearing cache when calling `ComparingByMembers` --- Src/FluentAssertions/Common/TypeExtensions.cs | 8 ++-- ...elfReferenceEquivalencyAssertionOptions.cs | 2 + .../Steps/DictionaryInterfaceInfo.cs | 2 +- .../AssertionOptionsSpecs.cs | 41 ++++++++++++++++++- .../DictionaryExtensions.cs | 2 +- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/Src/FluentAssertions/Common/TypeExtensions.cs b/Src/FluentAssertions/Common/TypeExtensions.cs index 3d512e4055..82e56943eb 100644 --- a/Src/FluentAssertions/Common/TypeExtensions.cs +++ b/Src/FluentAssertions/Common/TypeExtensions.cs @@ -217,7 +217,7 @@ public static IEnumerable GetNonPrivateMembers(this Type typeToRefle public static IEnumerable GetNonPrivateProperties(this Type typeToReflect, MemberVisibility visibility) { - return NonPrivatePropertiesCache.GetOrAdd((typeToReflect, visibility), key => + return NonPrivatePropertiesCache.GetOrAdd((typeToReflect, visibility), static key => { IEnumerable query = from propertyInfo in GetPropertiesFromHierarchy(key.Type, key.Visibility) @@ -245,7 +245,7 @@ private static IEnumerable GetPropertiesFromHierarchy(Type typeToR public static IEnumerable GetNonPrivateFields(this Type typeToReflect, MemberVisibility visibility) { - return NonPrivateFieldsCache.GetOrAdd((typeToReflect, visibility), key => + return NonPrivateFieldsCache.GetOrAdd((typeToReflect, visibility), static key => { IEnumerable query = from fieldInfo in GetFieldsFromHierarchy(key.Type, key.Visibility) @@ -533,7 +533,7 @@ public static MethodInfo GetImplicitConversionOperator(this Type type, Type sour public static bool HasValueSemantics(this Type type) { - return HasValueSemanticsCache.GetOrAdd(type, t => + return HasValueSemanticsCache.GetOrAdd(type, static t => t.OverridesEquals() && !t.IsAnonymousType() && !t.IsTuple() && @@ -583,7 +583,7 @@ private static bool IsAnonymousType(this Type type) public static bool IsRecord(this Type type) { - return TypeIsRecordCache.GetOrAdd(type, t => + return TypeIsRecordCache.GetOrAdd(type, static t => t.GetMethod("$") is not null && t.GetTypeInfo() .DeclaredProperties diff --git a/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs b/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs index 22077ea8bb..8e0df96248 100644 --- a/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs +++ b/Src/FluentAssertions/Equivalency/SelfReferenceEquivalencyAssertionOptions.cs @@ -166,6 +166,8 @@ IEnumerable IEquivalencyAssertionOptions.SelectionRules EqualityStrategy IEquivalencyAssertionOptions.GetEqualityStrategy(Type requestedType) { + // As the valueFactory parameter captures instance members, + // be aware if the cache must be cleared on mutating the members. return equalityStrategyCache.GetOrAdd(requestedType, type => { EqualityStrategy strategy; diff --git a/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs b/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs index c59c576367..6a92d1a1e4 100644 --- a/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs +++ b/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs @@ -95,7 +95,7 @@ public static bool TryGetFromWithKey(Type target, string role, Type key, out Dic private static DictionaryInterfaceInfo[] GetDictionaryInterfacesFrom(Type target) { - return Cache.GetOrAdd(target, key => + return Cache.GetOrAdd(target, static key => { if (Type.GetTypeCode(key) != TypeCode.Object) { diff --git a/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs b/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs index f732cfb4b8..541f1ab5dd 100644 --- a/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs +++ b/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs @@ -54,9 +54,46 @@ public void It_should_not_throw() } [Collection("AssertionOptionsSpecs")] - public class When_modifying_global_settings_a_previous_assertion_should_not_have_any_effect : Given_temporary_global_assertion_options + public class When_modifying_global_reference_type_settings_a_previous_assertion_should_not_have_any_effect + : Given_temporary_global_assertion_options { - public When_modifying_global_settings_a_previous_assertion_should_not_have_any_effect() + public When_modifying_global_reference_type_settings_a_previous_assertion_should_not_have_any_effect() + { + Given(() => + { + // Trigger a first equivalency check using the default global settings + new MyValueType { Value = 1 }.Should().BeEquivalentTo(new MyValueType { Value = 2 }); + }); + + When(() => + { + AssertionOptions.AssertEquivalencyUsing(o => o.ComparingByMembers()); + }); + } + + [Fact] + public void It_should_try_to_compare_the_classes_by_member_semantics_and_thus_throw() + { + Action act = () => new MyValueType { Value = 1 }.Should().BeEquivalentTo(new MyValueType { Value = 2 }); + + act.Should().Throw(); + } + } + + internal class MyValueType + { + public int Value { get; set; } + + public override bool Equals(object obj) => true; + + public override int GetHashCode() => 0; + } + + [Collection("AssertionOptionsSpecs")] + public class When_modifying_global_value_type_settings_a_previous_assertion_should_not_have_any_effect + : Given_temporary_global_assertion_options + { + public When_modifying_global_value_type_settings_a_previous_assertion_should_not_have_any_effect() { Given(() => { diff --git a/Tests/FluentAssertions.Specs/CultureAwareTesting/DictionaryExtensions.cs b/Tests/FluentAssertions.Specs/CultureAwareTesting/DictionaryExtensions.cs index 0c505d6d69..ec13b58de9 100644 --- a/Tests/FluentAssertions.Specs/CultureAwareTesting/DictionaryExtensions.cs +++ b/Tests/FluentAssertions.Specs/CultureAwareTesting/DictionaryExtensions.cs @@ -10,7 +10,7 @@ internal static class DictionaryExtensions public static TValue GetOrAdd(this IDictionary dictionary, TKey key) where TValue : new() => - dictionary.GetOrAdd(key, () => new TValue()); + dictionary.GetOrAdd(key, static () => new TValue()); public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func newValue) {