Skip to content

Commit

Permalink
Merge 14babeb into 721975a
Browse files Browse the repository at this point in the history
  • Loading branch information
logiclrd committed Mar 16, 2022
2 parents 721975a + 14babeb commit facfaa4
Show file tree
Hide file tree
Showing 20 changed files with 780 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ public IEnumerable<IEquivalencyStep> UserEquivalencySteps

public MemberVisibility IncludedFields => inner.IncludedFields;

public bool IgnoreNonBrowsableOnSubject => inner.IgnoreNonBrowsableOnSubject;

public bool ExcludeNonBrowsableOnExpectation => inner.ExcludeNonBrowsableOnExpectation;

public bool CompareRecordsByValue => inner.CompareRecordsByValue;

public EqualityStrategy GetEqualityStrategy(Type type)
Expand Down
15 changes: 15 additions & 0 deletions Src/FluentAssertions/Equivalency/Field.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Reflection;
using FluentAssertions.Common;

Expand All @@ -10,6 +11,7 @@ namespace FluentAssertions.Equivalency
public class Field : Node, IMember
{
private readonly FieldInfo fieldInfo;
private bool? isBrowsable;

public Field(FieldInfo fieldInfo, INode parent)
: this(fieldInfo.ReflectedType, fieldInfo, parent)
Expand Down Expand Up @@ -42,5 +44,18 @@ public object GetValue(object obj)
public CSharpAccessModifier GetterAccessibility => fieldInfo.GetCSharpAccessModifier();

public CSharpAccessModifier SetterAccessibility => fieldInfo.GetCSharpAccessModifier();

public bool IsBrowsable
{
get
{
if (isBrowsable == null)
{
isBrowsable = fieldInfo.GetCustomAttribute<EditorBrowsableAttribute>() is not { State: EditorBrowsableState.Never };
}

return isBrowsable.Value;
}
}
}
}
14 changes: 14 additions & 0 deletions Src/FluentAssertions/Equivalency/IEquivalencyAssertionOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;

using FluentAssertions.Equivalency.Tracing;

namespace FluentAssertions.Equivalency
Expand Down Expand Up @@ -71,6 +73,18 @@ public interface IEquivalencyAssertionOptions
/// </summary>
MemberVisibility IncludedFields { get; }

/// <summary>
/// Gets a value indicating whether members on the subject marked with [EditorBrowsable]
/// and an EditorBrowsableState of Never should be treated as though they don't exist.
/// </summary>
bool IgnoreNonBrowsableOnSubject { get; }

/// <summary>
/// Gets a value indicating whether members on the expectation marked with [EditorBrowsable]
/// and an EditorBrowsableState of Never should be excluded.
/// </summary>
bool ExcludeNonBrowsableOnExpectation { get; }

/// <summary>
/// Gets a value indicating whether records should be compared by value instead of their members
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions Src/FluentAssertions/Equivalency/IMember.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,10 @@ public interface IMember : INode
/// Gets the access modifier for the setter of this member.
/// </summary>
CSharpAccessModifier SetterAccessibility { get; }

/// <summary>
/// Gets a value indicating whether the member is browsable in the source code editor. This is controlled with the [EditorBrowsable] attribute.
/// </summary>
bool IsBrowsable { get; }
}
}
15 changes: 15 additions & 0 deletions Src/FluentAssertions/Equivalency/Property.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel;
using System.Reflection;
using FluentAssertions.Common;

Expand All @@ -11,6 +12,7 @@ namespace FluentAssertions.Equivalency
public class Property : Node, IMember
{
private readonly PropertyInfo propertyInfo;
private bool? isBrowsable;

public Property(PropertyInfo propertyInfo, INode parent)
: this(propertyInfo.ReflectedType, propertyInfo, parent)
Expand Down Expand Up @@ -43,5 +45,18 @@ public object GetValue(object obj)
public CSharpAccessModifier GetterAccessibility => propertyInfo.GetGetMethod(nonPublic: true).GetCSharpAccessModifier();

public CSharpAccessModifier SetterAccessibility => propertyInfo.GetSetMethod(nonPublic: true).GetCSharpAccessModifier();

public bool IsBrowsable
{
get
{
if (isBrowsable == null)
{
isBrowsable = propertyInfo.GetCustomAttribute<EditorBrowsableAttribute>() is not { State: EditorBrowsableState.Never };
}

return isBrowsable.Value;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace FluentAssertions.Equivalency.Selection
{
internal class ExcludeNonBrowsableMembersRule : IMemberSelectionRule
{
public bool IncludesMembers => false;

public IEnumerable<IMember> SelectMembers(INode currentNode, IEnumerable<IMember> selectedMembers, MemberSelectionContext context)
{
return selectedMembers.Where(member => member.IsBrowsable);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public abstract class SelfReferenceEquivalencyAssertionOptions<TSelf> : IEquival

private MemberVisibility includedProperties;
private MemberVisibility includedFields;
private bool ignoreNonBrowsableOnSubject;
private bool excludeNonBrowsableOnExpectation;

private bool compareRecordsByValue;

Expand All @@ -80,6 +82,8 @@ protected SelfReferenceEquivalencyAssertionOptions(IEquivalencyAssertionOptions
useRuntimeTyping = defaults.UseRuntimeTyping;
includedProperties = defaults.IncludedProperties;
includedFields = defaults.IncludedFields;
ignoreNonBrowsableOnSubject = defaults.IgnoreNonBrowsableOnSubject;
excludeNonBrowsableOnExpectation = defaults.ExcludeNonBrowsableOnExpectation;
compareRecordsByValue = defaults.CompareRecordsByValue;

ConversionSelector = defaults.ConversionSelector.Clone();
Expand Down Expand Up @@ -115,6 +119,11 @@ IEnumerable<IMemberSelectionRule> IEquivalencyAssertionOptions.SelectionRules
yield return new AllFieldsSelectionRule();
}

if (excludeNonBrowsableOnExpectation)
{
yield return new ExcludeNonBrowsableMembersRule();
}

foreach (IMemberSelectionRule rule in selectionRules)
{
yield return rule;
Expand Down Expand Up @@ -162,6 +171,10 @@ IEnumerable<IMemberSelectionRule> IEquivalencyAssertionOptions.SelectionRules

MemberVisibility IEquivalencyAssertionOptions.IncludedFields => includedFields;

bool IEquivalencyAssertionOptions.IgnoreNonBrowsableOnSubject => ignoreNonBrowsableOnSubject;

bool IEquivalencyAssertionOptions.ExcludeNonBrowsableOnExpectation => excludeNonBrowsableOnExpectation;

public bool CompareRecordsByValue => compareRecordsByValue;

EqualityStrategy IEquivalencyAssertionOptions.GetEqualityStrategy(Type requestedType)
Expand Down Expand Up @@ -312,6 +325,34 @@ public TSelf ExcludingProperties()
return (TSelf)this;
}

/// <summary>
/// Instructs the comparison to include non-browsable members (members with an EditorBrowsableState of Never).
/// </summary>
/// <returns></returns>
public TSelf IncludingNonBrowsableMembers()
{
excludeNonBrowsableOnExpectation = false;
return (TSelf)this;
}

/// <summary>
/// Instructs the comparison to exclude non-browsable members (members with an EditorBrowsableState of Never).
/// They can be marked non-browsable in the subject or the expectation. It is not required that they be marked
/// non-browsable in both the subject and the expectation.
/// </summary>
/// <returns></returns>
public TSelf ExcludingNonBrowsableMembers()
{
excludeNonBrowsableOnExpectation = true;
return (TSelf)this;
}

public TSelf IgnoringNonBrowsableMembersOnSubject()
{
ignoreNonBrowsableOnSubject = true;
return (TSelf)this;
}

/// <summary>
/// Instructs the comparison to respect the expectation's runtime type.
/// </summary>
Expand Down Expand Up @@ -698,6 +739,11 @@ public override string ToString()
.Append(useRuntimeTyping ? "runtime" : "declared")
.AppendLine(" types and members");

if (ignoreNonBrowsableOnSubject)
{
builder.AppendLine("- Do not consider members marked non-browsable on the subject");
}

if (isRecursive)
{
if (allowInfiniteRecursion)
Expand Down Expand Up @@ -737,6 +783,15 @@ public override string ToString()
builder.AppendLine($"- Compare {type} by its members");
}

if (excludeNonBrowsableOnExpectation)
{
builder.AppendLine("- Exclude non-browsable members");
}
else
{
builder.AppendLine("- Include non-browsable members");
}

foreach (IMemberSelectionRule rule in selectionRules)
{
builder.Append("- ").AppendLine(rule.ToString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public class StructuralEqualityEquivalencyStep : IEquivalencyStep
where match is not null
select match;

if (config.IgnoreNonBrowsableOnSubject)
{
query = query.Where(member => member.IsBrowsable);
}

return query.FirstOrDefault();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; set; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
Expand All @@ -823,6 +824,8 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.ConversionSelector ConversionSelector { get; }
FluentAssertions.Equivalency.CyclicReferenceHandling CyclicReferenceHandling { get; }
FluentAssertions.Equivalency.EnumEquivalencyHandling EnumEquivalencyHandling { get; }
bool ExcludeNonBrowsableOnExpectation { get; }
bool IgnoreNonBrowsableOnSubject { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedFields { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedProperties { get; }
bool IsRecursive { get; }
Expand Down Expand Up @@ -858,6 +861,7 @@ namespace FluentAssertions.Equivalency
{
System.Type DeclaringType { get; }
FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
bool IsBrowsable { get; }
System.Type ReflectedType { get; }
FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
object GetValue(object obj);
Expand Down Expand Up @@ -961,6 +965,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
Expand Down Expand Up @@ -989,15 +994,18 @@ namespace FluentAssertions.Equivalency
public TSelf ExcludingFields() { }
public TSelf ExcludingMissingMembers() { }
public TSelf ExcludingNestedObjects() { }
public TSelf ExcludingNonBrowsableMembers() { }
public TSelf ExcludingProperties() { }
public TSelf IgnoringCyclicReferences() { }
public TSelf IgnoringNonBrowsableMembersOnSubject() { }
public TSelf Including(System.Linq.Expressions.Expression<System.Func<FluentAssertions.Equivalency.IMemberInfo, bool>> predicate) { }
public TSelf IncludingAllDeclaredProperties() { }
public TSelf IncludingAllRuntimeProperties() { }
public TSelf IncludingFields() { }
public TSelf IncludingInternalFields() { }
public TSelf IncludingInternalProperties() { }
public TSelf IncludingNestedObjects() { }
public TSelf IncludingNonBrowsableMembers() { }
public TSelf IncludingProperties() { }
public TSelf RespectingDeclaredTypes() { }
public TSelf RespectingRuntimeTypes() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; set; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
Expand All @@ -835,6 +836,8 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.ConversionSelector ConversionSelector { get; }
FluentAssertions.Equivalency.CyclicReferenceHandling CyclicReferenceHandling { get; }
FluentAssertions.Equivalency.EnumEquivalencyHandling EnumEquivalencyHandling { get; }
bool ExcludeNonBrowsableOnExpectation { get; }
bool IgnoreNonBrowsableOnSubject { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedFields { get; }
FluentAssertions.Equivalency.MemberVisibility IncludedProperties { get; }
bool IsRecursive { get; }
Expand Down Expand Up @@ -870,6 +873,7 @@ namespace FluentAssertions.Equivalency
{
System.Type DeclaringType { get; }
FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
bool IsBrowsable { get; }
System.Type ReflectedType { get; }
FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
object GetValue(object obj);
Expand Down Expand Up @@ -973,6 +977,7 @@ namespace FluentAssertions.Equivalency
public System.Type DeclaringType { get; }
public override string Description { get; }
public FluentAssertions.Common.CSharpAccessModifier GetterAccessibility { get; }
public bool IsBrowsable { get; }
public System.Type ReflectedType { get; }
public FluentAssertions.Common.CSharpAccessModifier SetterAccessibility { get; }
public object GetValue(object obj) { }
Expand Down Expand Up @@ -1001,15 +1006,18 @@ namespace FluentAssertions.Equivalency
public TSelf ExcludingFields() { }
public TSelf ExcludingMissingMembers() { }
public TSelf ExcludingNestedObjects() { }
public TSelf ExcludingNonBrowsableMembers() { }
public TSelf ExcludingProperties() { }
public TSelf IgnoringCyclicReferences() { }
public TSelf IgnoringNonBrowsableMembersOnSubject() { }
public TSelf Including(System.Linq.Expressions.Expression<System.Func<FluentAssertions.Equivalency.IMemberInfo, bool>> predicate) { }
public TSelf IncludingAllDeclaredProperties() { }
public TSelf IncludingAllRuntimeProperties() { }
public TSelf IncludingFields() { }
public TSelf IncludingInternalFields() { }
public TSelf IncludingInternalProperties() { }
public TSelf IncludingNestedObjects() { }
public TSelf IncludingNonBrowsableMembers() { }
public TSelf IncludingProperties() { }
public TSelf RespectingDeclaredTypes() { }
public TSelf RespectingRuntimeTypes() { }
Expand Down
Loading

0 comments on commit facfaa4

Please sign in to comment.