Skip to content

Commit 02e71b3

Browse files
committed
Improved the robustness of the cyclic detection algorithm. Closes #78
1 parent 0f24a12 commit 02e71b3

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

FluentAssertions.Core/Equivalency/ObjectReference.cs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23

34
using FluentAssertions.Common;
45

@@ -10,12 +11,12 @@ namespace FluentAssertions.Equivalency
1011
internal class ObjectReference
1112
{
1213
private readonly object @object;
13-
private readonly string propertyPath;
14+
private readonly string[] path;
1415

15-
public ObjectReference(object @object, string propertyPath)
16+
public ObjectReference(object @object, string path)
1617
{
1718
this.@object = @object;
18-
this.propertyPath = propertyPath;
19+
this.path = path.ToLower().Split('.');
1920
}
2021

2122
/// <summary>
@@ -29,12 +30,13 @@ public override bool Equals(object obj)
2930
{
3031
var other = (ObjectReference)obj;
3132

32-
int firstDot = other.propertyPath.IndexOf(".");
33-
string firstPartOfOtherPropertyPath = (firstDot >= 0) ? other.propertyPath.Substring(0, firstDot) : "";
33+
return ReferenceEquals(@object, other.@object) && IsParentOf(other);
3434

35-
return ReferenceEquals(@object, other.@object) &&
36-
(propertyPath.Length > 0) &&
37-
firstPartOfOtherPropertyPath.Equals(propertyPath, StringComparison.CurrentCultureIgnoreCase);
35+
}
36+
37+
private bool IsParentOf(ObjectReference other)
38+
{
39+
return (other.path.Length > path.Length) && other.path.Take(path.Length).SequenceEqual(path);
3840
}
3941

4042
/// <summary>
@@ -48,10 +50,15 @@ public override int GetHashCode()
4850
{
4951
unchecked
5052
{
51-
return (@object.GetHashCode() * 397) ^ propertyPath.GetHashCode();
53+
return (@object.GetHashCode() * 397) ^ path.GetHashCode();
5254
}
5355
}
5456

57+
public override string ToString()
58+
{
59+
return string.Format("{{\"{0}\", {1}}}", path, @object);
60+
}
61+
5562
public bool IsReference
5663
{
5764
get { return !ReferenceEquals(@object, null) && @object.GetType().IsComplexType(); }

FluentAssertions.Net40.Specs/EquivalencySpecs.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2550,6 +2550,55 @@ public LogbookCode(string key)
25502550
public string Key { get; protected set; }
25512551
}
25522552

2553+
private class MyCompany
2554+
{
2555+
public string Name { get; set; }
2556+
public MyCompanyLogo Logo { get; set; }
2557+
public List<MyUser> Users { get; set; }
2558+
}
2559+
2560+
private class MyUser
2561+
{
2562+
public string Name { get; set; }
2563+
public MyCompany Company { get; set; }
2564+
}
2565+
2566+
private class MyCompanyLogo
2567+
{
2568+
public string Url { get; set; }
2569+
public MyCompany Company { get; set; }
2570+
public MyUser CreatedBy { get; set; }
2571+
}
2572+
2573+
[TestMethod]
2574+
public void When_the_root_object_is_referenced_from_a_nested_object_it_should_treat_it_as_a_cyclic_reference()
2575+
{
2576+
//-----------------------------------------------------------------------------------------------------------
2577+
// Arrange
2578+
//-----------------------------------------------------------------------------------------------------------
2579+
var company1 = new MyCompany { Name = "Company" };
2580+
var user1 = new MyUser { Name = "User", Company = company1 };
2581+
company1.Users = new List<MyUser> { user1 };
2582+
var logo1 = new MyCompanyLogo { Url = "blank", Company = company1, CreatedBy = user1 };
2583+
company1.Logo = logo1;
2584+
2585+
var company2 = new MyCompany { Name = "Company" };
2586+
var user2 = new MyUser { Name = "User", Company = company2 };
2587+
company2.Users = new List<MyUser> { user2 };
2588+
var logo2 = new MyCompanyLogo { Url = "blank", Company = company2, CreatedBy = user2 };
2589+
company2.Logo = logo2;
2590+
2591+
//-----------------------------------------------------------------------------------------------------------
2592+
// Act
2593+
//-----------------------------------------------------------------------------------------------------------
2594+
Action action = () => company1.ShouldBeEquivalentTo(company2, o => o.IgnoringCyclicReferences());
2595+
2596+
//-----------------------------------------------------------------------------------------------------------
2597+
// Assert
2598+
//-----------------------------------------------------------------------------------------------------------
2599+
action.ShouldNotThrow();
2600+
}
2601+
25532602
#endregion
25542603

25552604
#region Nested Enumerables

0 commit comments

Comments
 (0)