Skip to content

Commit

Permalink
SAVEPOINT
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisdoomen committed Apr 27, 2024
1 parent 549f752 commit 037a377
Show file tree
Hide file tree
Showing 160 changed files with 1,811 additions and 2,715 deletions.
1 change: 1 addition & 0 deletions FluentAssertions.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseExplicitType</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FCONSTRUCTOR/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
Expand Down
10 changes: 5 additions & 5 deletions Src/FluentAssertions/AndWhich.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ namespace FluentAssertions;

public class AndWhich<TParent, TSubject> : AndConstraint<TParent>
{
private readonly Assertion assertion;
private readonly AssertionChain assertionChain;
private readonly string pathPostfix;
private readonly Lazy<TSubject> getSubject;

public AndWhich(TParent parent, TSubject subject, Assertion assertion, string pathPostfix)
public AndWhich(TParent parent, TSubject subject, AssertionChain assertionChain, string pathPostfix)
: base(parent)
{
getSubject = new Lazy<TSubject>(() => subject);

this.assertion = assertion;
this.assertionChain = assertionChain;
this.pathPostfix = pathPostfix;
}

Expand All @@ -43,9 +43,9 @@ public TSubject Which
{
get
{
assertion.AddCallerPostfix(pathPostfix);
assertionChain.AddCallerPostfix(pathPostfix);

Assertion.ReuseOnce(assertion);
AssertionChain.ReuseOnce(assertionChain);

return getSubject.Value;
}
Expand Down
202 changes: 68 additions & 134 deletions Src/FluentAssertions/AssertionExtensions.cs

Large diffs are not rendered by default.

288 changes: 144 additions & 144 deletions Src/FluentAssertions/Collections/GenericCollectionAssertions.cs

Large diffs are not rendered by default.

108 changes: 54 additions & 54 deletions Src/FluentAssertions/Collections/GenericDictionaryAssertions.cs

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions Src/FluentAssertions/Collections/StringCollectionAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public class StringCollectionAssertions : StringCollectionAssertions<IEnumerable
/// <summary>
/// Initializes a new instance of the <see cref="StringCollectionAssertions"/> class.
/// </summary>
public StringCollectionAssertions(IEnumerable<string> actualValue, Assertion assertion)
: base(actualValue, assertion)
public StringCollectionAssertions(IEnumerable<string> actualValue, AssertionChain assertionChain)
: base(actualValue, assertionChain)
{
}
}
Expand All @@ -25,8 +25,8 @@ public class StringCollectionAssertions<TCollection>
/// <summary>
/// Initializes a new instance of the <see cref="StringCollectionAssertions{TCollection}"/> class.
/// </summary>
public StringCollectionAssertions(TCollection actualValue, Assertion assertion)
: base(actualValue, assertion)
public StringCollectionAssertions(TCollection actualValue, AssertionChain assertionChain)
: base(actualValue, assertionChain)
{
}
}
Expand All @@ -35,15 +35,15 @@ public class StringCollectionAssertions<TCollection, TAssertions> : GenericColle
where TCollection : IEnumerable<string>
where TAssertions : StringCollectionAssertions<TCollection, TAssertions>
{
private readonly Assertion assertion;
private readonly AssertionChain assertionChain;

/// <summary>
/// Initializes a new instance of the <see cref="StringCollectionAssertions{TCollection, TAssertions}"/> class.
/// </summary>
public StringCollectionAssertions(TCollection actualValue, Assertion assertion)
: base(actualValue, assertion)
public StringCollectionAssertions(TCollection actualValue, AssertionChain assertionChain)
: base(actualValue, assertionChain)
{
this.assertion = assertion;
this.assertionChain = assertionChain;
}

/// <summary>
Expand Down Expand Up @@ -251,16 +251,16 @@ public AndConstraint<TAssertions> BeEquivalentTo(params string[] expectation)
Guard.ThrowIfArgumentIsEmpty(wildcardPattern, nameof(wildcardPattern),
"Cannot match strings in collection against an empty string. Provide a wildcard pattern or use the Contain method.");

assertion
assertionChain
.BecauseOf(because, becauseArgs)
.ForCondition(Subject is not null)
.FailWith("Expected {context:collection} to contain a match of {0}{reason}, but found <null>.", wildcardPattern);

IEnumerable<string> matched = [];

if (assertion.Succeeded)
if (assertionChain.Succeeded)
{
assertion
assertionChain
.BecauseOf(because, becauseArgs)
.ForCondition(ContainsMatch(wildcardPattern))
.FailWith("Expected {context:collection} {0} to contain a match of {1}{reason}.", Subject, wildcardPattern);
Expand Down Expand Up @@ -336,15 +336,15 @@ private IEnumerable<string> AllThatMatch(string wildcardPattern)
Guard.ThrowIfArgumentIsEmpty(wildcardPattern, nameof(wildcardPattern),
"Cannot match strings in collection against an empty string. Provide a wildcard pattern or use the NotContain method.");

assertion
assertionChain
.BecauseOf(because, becauseArgs)
.ForCondition(Subject is not null)
.FailWith("Did not expect {context:collection} to contain a match of {0}{reason}, but found <null>.",
wildcardPattern);

if (assertion.Succeeded)
if (assertionChain.Succeeded)
{
assertion
assertionChain
.BecauseOf(because, becauseArgs)
.ForCondition(NotContainsMatch(wildcardPattern))
.FailWith("Did not expect {context:collection} {0} to contain a match of {1}{reason}.", Subject, wildcardPattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace FluentAssertions.Collections;
public class SubsequentOrderingAssertions<T>
: SubsequentOrderingGenericCollectionAssertions<IEnumerable<T>, T, SubsequentOrderingAssertions<T>>
{
public SubsequentOrderingAssertions(IEnumerable<T> actualValue, IOrderedEnumerable<T> previousOrderedEnumerable, Assertion assertion)
: base(actualValue, previousOrderedEnumerable, assertion)
public SubsequentOrderingAssertions(IEnumerable<T> actualValue, IOrderedEnumerable<T> previousOrderedEnumerable, AssertionChain assertionChain)
: base(actualValue, previousOrderedEnumerable, assertionChain)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public class SubsequentOrderingGenericCollectionAssertions<TCollection, T, TAsse
private readonly IOrderedEnumerable<T> previousOrderedEnumerable;
private bool subsequentOrdering;

public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable<T> previousOrderedEnumerable, Assertion assertion)
: base(actualValue, assertion)
public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable<T> previousOrderedEnumerable, AssertionChain assertionChain)
: base(actualValue, assertionChain)
{
this.previousOrderedEnumerable = previousOrderedEnumerable;
}
Expand Down Expand Up @@ -168,8 +168,8 @@ public class SubsequentOrderingGenericCollectionAssertions<TCollection, T>
: SubsequentOrderingGenericCollectionAssertions<TCollection, T, SubsequentOrderingGenericCollectionAssertions<TCollection, T>>
where TCollection : IEnumerable<T>
{
public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable<T> previousOrderedEnumerable, Assertion assertion)
: base(actualValue, previousOrderedEnumerable, assertion)
public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable<T> previousOrderedEnumerable, AssertionChain assertionChain)
: base(actualValue, previousOrderedEnumerable, assertionChain)
{
}
}
4 changes: 2 additions & 2 deletions Src/FluentAssertions/EnumAssertionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static class EnumAssertionsExtensions
public static EnumAssertions<TEnum> Should<TEnum>(this TEnum @enum)
where TEnum : struct, Enum
{
return new EnumAssertions<TEnum>(@enum, Assertion.GetOrCreate());
return new EnumAssertions<TEnum>(@enum, AssertionChain.GetOrCreate());
}

/// <summary>
Expand All @@ -31,6 +31,6 @@ public static EnumAssertions<TEnum> Should<TEnum>(this TEnum @enum)
public static NullableEnumAssertions<TEnum> Should<TEnum>(this TEnum? @enum)
where TEnum : struct, Enum
{
return new NullableEnumAssertions<TEnum>(@enum, Assertion.GetOrCreate());
return new NullableEnumAssertions<TEnum>(@enum, AssertionChain.GetOrCreate());
}
}
6 changes: 3 additions & 3 deletions Src/FluentAssertions/Equivalency/EquivalencyStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ namespace FluentAssertions.Equivalency;
/// </summary>
public abstract class EquivalencyStep<T> : IEquivalencyStep
{
public EquivalencyResult Handle(Comparands comparands, Assertion assertion, IEquivalencyValidationContext context,
public EquivalencyResult Handle(Comparands comparands, AssertionChain assertionChain, IEquivalencyValidationContext context,
IValidateChildNodeEquivalency nestedValidator)
{
if (!typeof(T).IsAssignableFrom(comparands.GetExpectedType(context.Options)))
{
return EquivalencyResult.ContinueWithNext;
}

return OnHandle(comparands, assertion, context, nestedValidator);
return OnHandle(comparands, assertionChain, context, nestedValidator);
}

/// <summary>
/// Implements <see cref="IEquivalencyStep.Handle"/>, but only gets called when the expected type matches <typeparamref name="T"/>.
/// </summary>
protected abstract EquivalencyResult OnHandle(Comparands comparands, Assertion assertion,
protected abstract EquivalencyResult OnHandle(Comparands comparands, AssertionChain assertionChain,
IEquivalencyValidationContext context,
IValidateChildNodeEquivalency nestedValidator);
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public bool IsCyclicReference(object expectation)
is EqualityStrategy.Members or EqualityStrategy.ForceMembers;

var reference = new ObjectReference(expectation, CurrentNode.PathAndName, compareByMembers);
return CyclicReferenceDetector.IsCyclicReference(reference, Options.CyclicReferenceHandling, Reason);
return CyclicReferenceDetector.IsCyclicReference(reference);
}

public ITraceWriter TraceWriter { get; set; }
Expand Down
40 changes: 24 additions & 16 deletions Src/FluentAssertions/Equivalency/EquivalencyValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ public void AssertEquality(Comparands comparands, EquivalencyValidationContext c
{
using var scope = new AssertionScope();

var getIdentifierOnce = new Lazy<string>(() => scope.GetIdentifier());

var assertion = Assertion.GetOrCreate(() => scope, () => getIdentifierOnce.Value);
var assertion = AssertionChain.GetOrCreate();

assertion.WithReportable("configuration", () => context.Options.ToString());
assertion.BecauseOf(context.Reason);
Expand All @@ -30,52 +28,62 @@ public void AssertEquality(Comparands comparands, EquivalencyValidationContext c
}
}

private void RecursivelyAssertEquivalencyOf(Comparands comparands, Assertion assertion, IEquivalencyValidationContext context)
private void RecursivelyAssertEquivalencyOf(Comparands comparands, AssertionChain assertionChain, IEquivalencyValidationContext context)
{
AssertEquivalencyOf(comparands, assertion, context);
AssertEquivalencyOf(comparands, assertionChain, context);
}

public void AssertEquivalencyOf(Comparands comparands, Assertion assertion, IEquivalencyValidationContext context)
public void AssertEquivalencyOf(Comparands comparands, AssertionChain assertionChain, IEquivalencyValidationContext context)
{
if (ShouldContinueThisDeep(context.CurrentNode, context.Options, assertion))
if (ShouldContinueThisDeep(context.CurrentNode, context.Options, assertionChain))
{
TrackWhatIsNeededToProvideContextToFailures(assertion, comparands, context.CurrentNode);
TrackWhatIsNeededToProvideContextToFailures(assertionChain, comparands, context.CurrentNode);

if (!context.IsCyclicReference(comparands.Expectation))
{
TryToProveNodesAreEquivalent(assertion, comparands, context);
TryToProveNodesAreEquivalent(assertionChain, comparands, context);
}
else if (context.Options.CyclicReferenceHandling == CyclicReferenceHandling.ThrowException)
{
assertionChain
.BecauseOf(context.Reason)
.FailWith("Expected {context:subject} to be {expectation}{reason}, but it contains a cyclic reference.");
}
else
{
// If cyclic references are allowed, we consider the objects to be equivalent
}
}
}

private static bool ShouldContinueThisDeep(INode currentNode, IEquivalencyOptions options,
Assertion assertion)
AssertionChain assertionChain)
{
bool shouldRecurse = options.AllowInfiniteRecursion || currentNode.Depth <= MaxDepth;
if (!shouldRecurse)
{
// This will throw, unless we're inside an AssertionScope
assertion.FailWith($"The maximum recursion depth of {MaxDepth} was reached. ");
assertionChain.FailWith($"The maximum recursion depth of {MaxDepth} was reached. ");
}

return shouldRecurse;
}

private static void TrackWhatIsNeededToProvideContextToFailures(Assertion assertion, Comparands comparands, INode currentNode)
private static void TrackWhatIsNeededToProvideContextToFailures(AssertionChain assertionChain, Comparands comparands, INode currentNode)
{
assertion.Context = new Lazy<string>(() => currentNode.Description);
assertion.TrackComparands(comparands.Subject, comparands.Expectation);
assertionChain.Context = new Lazy<string>(() => currentNode.Description);
assertionChain.TrackComparands(comparands.Subject, comparands.Expectation);
}

private void TryToProveNodesAreEquivalent(Assertion assertion, Comparands comparands, IEquivalencyValidationContext context)
private void TryToProveNodesAreEquivalent(AssertionChain assertionChain, Comparands comparands, IEquivalencyValidationContext context)
{
using var _ = context.Tracer.WriteBlock(node => node.Description);

Func<IEquivalencyStep, GetTraceMessage> getMessage = step => _ => $"Equivalency was proven by {step.GetType().Name}";

foreach (IEquivalencyStep step in AssertionOptions.EquivalencyPlan)
{
var result = step.Handle(comparands, assertion, context, this);
var result = step.Handle(comparands, assertionChain, context, this);
if (result == EquivalencyResult.EquivalencyProven)
{
context.Tracer.WriteLine(getMessage(step));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,13 @@ internal class CyclicReferenceDetector : ICloneable2
/// Determines whether the specified object reference is a cyclic reference to the same object earlier in the
/// equivalency validation.
/// </summary>
/// <remarks>
/// The behavior of a cyclic reference is determined by the <paramref name="handling"/> parameter.
/// </remarks>
public bool IsCyclicReference(ObjectReference reference, CyclicReferenceHandling handling, Reason reason = null)
public bool IsCyclicReference(ObjectReference reference)
{
bool isCyclic = false;

if (reference.CompareByMembers)
{
isCyclic = !observedReferences.Add(reference);

if (isCyclic && handling == CyclicReferenceHandling.ThrowException)
{
AssertionScope.Current
.BecauseOf(reason)
.FailWith(
"Expected {context:subject} to be {expectation}{reason}, but it contains a cyclic reference.");
}
}

return isCyclic;
Expand Down
2 changes: 1 addition & 1 deletion Src/FluentAssertions/Equivalency/IEquivalencyStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ public interface IEquivalencyStep
/// <remarks>
/// May throw when preconditions are not met or if it detects mismatching data.
/// </remarks>
EquivalencyResult Handle(Comparands comparands, Assertion assertion, IEquivalencyValidationContext context,
EquivalencyResult Handle(Comparands comparands, AssertionChain assertionChain, IEquivalencyValidationContext context,
IValidateChildNodeEquivalency nestedValidator);
}
4 changes: 2 additions & 2 deletions Src/FluentAssertions/Equivalency/IMemberMatchingRule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ public interface IMemberMatchingRule
/// </param>
/// <param name="parent"></param>
/// <param name="options"></param>
/// <param name="assertion"></param>
/// <param name="assertionChain"></param>
/// <returns>
/// Returns the <see cref="IMember"/> of the property with which to compare the subject with, or <see langword="null"/>
/// if no match was found.
/// </returns>
IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, Assertion assertion);
IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, AssertionChain assertionChain);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ public interface IValidateChildNodeEquivalency
/// <summary>
/// Runs a deep recursive equivalency assertion on the provided <paramref name="comparands"/>.
/// </summary>
void AssertEquivalencyOf(Comparands comparands, Assertion assertion, IEquivalencyValidationContext context);
void AssertEquivalencyOf(Comparands comparands, AssertionChain assertionChain, IEquivalencyValidationContext context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public MappedMemberMatchingRule(string expectationMemberName, string subjectMemb
this.subjectMemberName = subjectMemberName;
}

public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, Assertion assertion)
public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, AssertionChain assertionChain)
{
if (parent.Type.IsSameOrInherits(typeof(TExpectation)) && subject is TSubject &&
expectedMember.Name == expectationMemberName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public MappedPathMatchingRule(string expectationMemberPath, string subjectMember
}
}

public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, Assertion assertion)
public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, AssertionChain assertionChain)
{
MemberPath path = expectationPath;

Expand Down

0 comments on commit 037a377

Please sign in to comment.