From f5eb95e8cea7c0779dbe2ad6885fdfa159217fe6 Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Sun, 10 Nov 2024 21:00:07 +0100 Subject: [PATCH] The expectation node identified as a cyclic reference is still compared to the subject node using simple equality. --- Directory.Build.props | 2 +- .../Equivalency/EquivalencyValidator.cs | 19 ++++- .../CyclicReferencesSpecs.cs | 79 ++++++++++++++++++- docs/_pages/releases.md | 6 ++ 4 files changed, 100 insertions(+), 6 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 5c2d966030..c675073db2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -23,7 +23,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs index 233e558afa..230952a4aa 100644 --- a/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs @@ -35,7 +35,11 @@ public void RecursivelyAssertEquality(Comparands comparands, IEquivalencyValidat { TrackWhatIsNeededToProvideContextToFailures(scope, comparands, context.CurrentNode); - if (!context.IsCyclicReference(comparands.Expectation)) + if (context.IsCyclicReference(comparands.Expectation)) + { + AssertComparandsPointToActualObjects(comparands); + } + else { TryToProveNodesAreEquivalent(comparands, context); } @@ -62,6 +66,19 @@ private static void TrackWhatIsNeededToProvideContextToFailures(AssertionScope s scope.TrackComparands(comparands.Subject, comparands.Expectation); } + private static void AssertComparandsPointToActualObjects(Comparands comparands) + { + if (ReferenceEquals(comparands.Subject, comparands.Expectation)) + { + return; + } + + if (comparands.Subject is null) + { + comparands.Subject.Should().BeSameAs(comparands.Expectation); + } + } + private void TryToProveNodesAreEquivalent(Comparands comparands, IEquivalencyValidationContext context) { using var _ = context.Tracer.WriteBlock(node => node.Description); diff --git a/Tests/FluentAssertions.Equivalency.Specs/CyclicReferencesSpecs.cs b/Tests/FluentAssertions.Equivalency.Specs/CyclicReferencesSpecs.cs index ca99887b63..b57ed1a148 100644 --- a/Tests/FluentAssertions.Equivalency.Specs/CyclicReferencesSpecs.cs +++ b/Tests/FluentAssertions.Equivalency.Specs/CyclicReferencesSpecs.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using JetBrains.Annotations; using Xunit; using Xunit.Sdk; @@ -68,6 +69,73 @@ public void By_default_cyclic_references_are_not_valid() .WithMessage("Expected property cyclicRoot.Level.Root to be*but it contains a cyclic reference*"); } + [Fact] + public void The_cyclic_reference_itself_will_be_compared_using_simple_equality() + { + // Arrange + var expectedChild = new Child + { + Stuff = 1 + }; + + var expectedParent = new Parent + { + Child1 = expectedChild + }; + + expectedChild.Parent = expectedParent; + + var actualChild = new Child + { + Stuff = 1 + }; + + var actualParent = new Parent + { + Child1 = actualChild + }; + + // Act + var act = () => actualParent.Should().BeEquivalentTo(expectedParent, options => options.IgnoringCyclicReferences()); + + // Assert + act.Should().Throw() + .WithMessage("Expected property actualParent.Child1.Parent to refer to*but found*null*"); + } + + [Fact] + public void The_cyclic_reference_can_be_ignored_if_the_comparands_point_to_the_same_object() + { + // Arrange + var expectedChild = new Child + { + Stuff = 1 + }; + + var expectedParent = new Parent + { + Child1 = expectedChild + }; + + expectedChild.Parent = expectedParent; + + var actualChild = new Child + { + Stuff = 1 + }; + + var actualParent = new Parent + { + Child1 = actualChild + }; + + // Connect this child to the same parent as the expectation child + actualChild.Parent = expectedParent; + + // Act + actualParent.Should().BeEquivalentTo(expectedParent, options => options.IgnoringCyclicReferences()); + } + [Fact] public void Two_graphs_with_ignored_cyclic_references_can_be_compared() { @@ -93,20 +161,23 @@ private class Parent { public Child Child1 { get; set; } + [UsedImplicitly] public Child Child2 { get; set; } } private class Child { - public Child(Parent parent, int stuff = 0) + public Child(Parent parent = null, int stuff = 0) { Parent = parent; Stuff = stuff; } - public Parent Parent { get; } + [UsedImplicitly] + public Parent Parent { get; set; } - public int Stuff { get; } + [UsedImplicitly] + public int Stuff { get; set; } } [Fact] @@ -216,7 +287,7 @@ public void Can_ignore_cyclic_references_for_inequivalency_assertions() // Act / Assert recursiveClass1.Should().NotBeEquivalentTo(recursiveClass2, - options => options.AllowingInfiniteRecursion()); + options => options.AllowingInfiniteRecursion()); } [Fact] diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index dacd0b0866..96620b92f4 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -7,6 +7,12 @@ sidebar: nav: "sidebar" --- +## 6.12.3 + +### Fixes + +* The expectation node identified as a cyclic reference is still compared to the subject node using simple equality - [2819](https://github.com/fluentassertions/fluentassertions/pull/2819) + ## 6.12.2 ### Fixes