Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 22 additions & 43 deletions Src/FluentAssertions/Execution/AssertionScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace FluentAssertions.Execution
/// Represents an implicit or explicit scope within which multiple assertions can be collected.
/// </summary>
/// <remarks>
/// This class is supposed to have a very short life time and is not safe to be used in assertion that cross thread-boundaries such as when
/// using <c>async</c> or <c>await</c>.
/// This class is supposed to have a very short life time and is not safe to be used in assertion that cross thread-boundaries
/// such as when using <c>async</c> or <c>await</c>.
/// </remarks>
public sealed class AssertionScope : IAssertionScope
{
Expand Down Expand Up @@ -84,7 +84,8 @@ public AssertionScope()
}

/// <summary>
/// Starts a named scope within which multiple assertions can be executed and which will not throw until the scope is disposed.
/// Starts a named scope within which multiple assertions can be executed
/// and which will not throw until the scope is disposed.
/// </summary>
public AssertionScope(string context)
: this()
Expand All @@ -96,7 +97,8 @@ public AssertionScope(string context)
}

/// <summary>
/// Starts a named scope within which multiple assertions can be executed and which will not throw until the scope is disposed.
/// Starts a named scope within which multiple assertions can be executed
/// and which will not throw until the scope is disposed.
/// </summary>
public AssertionScope(Lazy<string> context)
: this()
Expand All @@ -122,12 +124,7 @@ public static AssertionScope Current
private set => SetCurrentAssertionScope(value);
}

/// <summary>
/// Forces the formatters that support it to add the necessary line breaks.
/// </summary>
/// <remarks>
/// This is just shorthand for modifying the <see cref="FormattingOptions"/> property.
/// </remarks>
/// <inheritdoc cref="IAssertionScope.UsingLineBreaks"/>
public AssertionScope UsingLineBreaks
{
get
Expand Down Expand Up @@ -155,9 +152,7 @@ public AssertionScope BecauseOf(Reason reason)
return BecauseOf(reason.FormattedMessage, reason.Arguments);
}

/// <summary>
/// Adds an explanation of why the assertion is supposed to succeed to the scope.
/// </summary>
/// <inheritdoc cref="IAssertionScope.BecauseOf(string, object[])"/>
public AssertionScope BecauseOf(string because, params object[] becauseArgs)
{
reason = () =>
Expand All @@ -175,22 +170,7 @@ public AssertionScope BecauseOf(string because, params object[] becauseArgs)
return this;
}

/// <summary>
/// Sets the expectation part of the failure message when the assertion is not met.
/// </summary>
/// <remarks>
/// In addition to the numbered <see cref="string.Format(string,object[])"/>-style placeholders, messages may contain a few
/// specialized placeholders as well. For instance, {reason} will be replaced with the reason of the assertion as passed
/// to <see cref="BecauseOf(FluentAssertions.Execution.Reason)"/>. Other named placeholders will be replaced with the <see cref="Current"/> scope data
/// passed through <see cref="AddNonReportable"/> and <see cref="AddReportable(string,string)"/>. Finally, a description of the
/// current subject can be passed through the {context:description} placeholder. This is used in the message if no
/// explicit context is specified through the <see cref="AssertionScope"/> constructor.
/// Note that only 10 <paramref name="args"/> are supported in combination with a {reason}.
/// If an expectation was set through a prior call to <see cref="WithExpectation"/>, then the failure message is appended to that
/// expectation.
/// </remarks>
/// <param name="message">The format string that represents the failure message.</param>
/// <param name="args">Optional arguments to any numbered placeholders.</param>
/// <inheritdoc cref="IAssertionScope.WithExpectation(string, object[])"/>
public AssertionScope WithExpectation(string message, params object[] args)
{
Func<string> localReason = reason;
Expand All @@ -212,6 +192,7 @@ internal void TrackComparands(object subject, object expectation)
contextData.Add(new ContextDataItems.DataItem("expectation", expectation, reportable: false, requiresFormatting: true));
}

/// <inheritdoc/>
public Continuation ClearExpectation()
{
expectation = null;
Expand All @@ -225,6 +206,7 @@ public GivenSelector<T> Given<T>(Func<T> selector)
return new GivenSelector<T>(selector, this, continueAsserting: succeeded != false);
}

/// <inheritdoc cref="IAssertionScope.ForCondition(bool)"/>
public AssertionScope ForCondition(bool condition)
{
succeeded = condition;
Expand All @@ -234,8 +216,11 @@ public AssertionScope ForCondition(bool condition)

/// <summary>
/// Makes assertion fail when <paramref name="actualOccurrences"/> does not match <paramref name="constraint"/>.
/// The occurrence description in natural language could then be inserted in failure message by using {expectedOccurrence} placeholder in
/// message parameters of <see cref="FluentAssertions.Execution.AssertionScope.FailWith(string, object[])"/> and its overloaded versions.
/// <para>
/// The occurrence description in natural language could then be inserted in failure message by using
/// <em>{expectedOccurrence}</em> placeholder in message parameters of <see cref="FailWith(string, object[])"/> and its
/// overloaded versions.
/// </para>
/// </summary>
/// <param name="constraint"><see cref="OccurrenceConstraint"/> defining the number of expected occurrences.</param>
/// <param name="actualOccurrences">The number of actual occurrences.</param>
Expand All @@ -247,6 +232,7 @@ public AssertionScope ForConstraint(OccurrenceConstraint constraint, int actualO
return this;
}

/// <inheritdoc/>
public Continuation FailWith(Func<FailReason> failReasonFunc)
{
return FailWith(() =>
Expand Down Expand Up @@ -290,25 +276,19 @@ private Continuation FailWith(Func<string> failReasonFunc)
}
}

/// <summary>
/// Registers a failure message that does not contain any formatting placeholders.
/// </summary>
/// <inheritdoc/>
public Continuation FailWith(string message)
{
return FailWith(() => new FailReason(message, new object[0]));
}

/// <summary>
/// Registers a failure message with optional formatting arguments.
/// </summary>
/// <inheritdoc/>
public Continuation FailWith(string message, params object[] args)
{
return FailWith(() => new FailReason(message, args));
}

/// <summary>
/// Registers a failure message, but postpones evaluation of the formatting arguments until the assertion really fails.
/// </summary>
/// <inheritdoc/>
public Continuation FailWith(string message, params Func<object>[] argProviders)
{
return FailWith(() => new FailReason(message,
Expand Down Expand Up @@ -379,9 +359,7 @@ public T Get<T>(string key)
return contextData.Get<T>(key);
}

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <inheritdoc/>
public void Dispose()
{
SetCurrentAssertionScope(parent);
Expand All @@ -401,6 +379,7 @@ public void Dispose()
}
}

/// <inheritdoc cref="IAssertionScope.WithDefaultIdentifier(string)"/>
public AssertionScope WithDefaultIdentifier(string identifier)
{
fallbackIdentifier = identifier;
Expand Down
17 changes: 15 additions & 2 deletions Src/FluentAssertions/Execution/ContinuedAssertionScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ namespace FluentAssertions.Execution
/// Allows chaining multiple assertion scopes together using <see cref="Continuation.Then"/>.
/// </summary>
/// <remarks>
/// If the parent scope has captured a failed assertion, this class ensures that successive assertions
/// are no longer evaluated.
/// If the parent scope has captured a failed assertion,
/// this class ensures that successive assertions are no longer evaluated.
/// </remarks>
public sealed class ContinuedAssertionScope : IAssertionScope
{
Expand All @@ -20,6 +20,7 @@ internal ContinuedAssertionScope(AssertionScope predecessor, bool continueAssert
this.continueAsserting = continueAsserting;
}

/// <inheritdoc/>
public GivenSelector<T> Given<T>(Func<T> selector)
{
if (continueAsserting)
Expand All @@ -30,6 +31,7 @@ public GivenSelector<T> Given<T>(Func<T> selector)
return new GivenSelector<T>(() => default, predecessor, continueAsserting: false);
}

/// <inheritdoc/>
public IAssertionScope ForCondition(bool condition)
{
if (continueAsserting)
Expand All @@ -40,6 +42,7 @@ public IAssertionScope ForCondition(bool condition)
return this;
}

/// <inheritdoc/>
public Continuation FailWith(string message)
{
if (continueAsserting)
Expand All @@ -50,6 +53,7 @@ public Continuation FailWith(string message)
return new Continuation(predecessor, continueAsserting: false);
}

/// <inheritdoc/>
public Continuation FailWith(string message, params Func<object>[] argProviders)
{
if (continueAsserting)
Expand All @@ -60,6 +64,7 @@ public Continuation FailWith(string message, params Func<object>[] argProviders)
return new Continuation(predecessor, continueAsserting: false);
}

/// <inheritdoc/>
public Continuation FailWith(Func<FailReason> failReasonFunc)
{
if (continueAsserting)
Expand All @@ -70,6 +75,7 @@ public Continuation FailWith(Func<FailReason> failReasonFunc)
return new Continuation(predecessor, continueAsserting: false);
}

/// <inheritdoc/>
public Continuation FailWith(string message, params object[] args)
{
if (continueAsserting)
Expand All @@ -80,6 +86,7 @@ public Continuation FailWith(string message, params object[] args)
return new Continuation(predecessor, continueAsserting: false);
}

/// <inheritdoc/>
public IAssertionScope BecauseOf(string because, params object[] becauseArgs)
{
if (continueAsserting)
Expand All @@ -90,30 +97,36 @@ public IAssertionScope BecauseOf(string because, params object[] becauseArgs)
return this;
}

/// <inheritdoc/>
public Continuation ClearExpectation()
{
predecessor.ClearExpectation();

return new Continuation(predecessor, continueAsserting);
}

/// <inheritdoc/>
public IAssertionScope WithExpectation(string message, params object[] args)
{
return predecessor.WithExpectation(message, args);
}

/// <inheritdoc/>
public IAssertionScope WithDefaultIdentifier(string identifier)
{
return predecessor.WithDefaultIdentifier(identifier);
}

/// <inheritdoc/>
public IAssertionScope UsingLineBreaks => predecessor.UsingLineBreaks;

/// <inheritdoc/>
public string[] Discard()
{
return predecessor.Discard();
}

/// <inheritdoc/>
public void Dispose()
{
predecessor.Dispose();
Expand Down
40 changes: 24 additions & 16 deletions Src/FluentAssertions/Execution/FailReason.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
namespace FluentAssertions.Execution
{
/// <summary>
/// Represents assertion fail reason. Contains the message and arguments for
/// message's numbered placeholders.
/// Represents assertion fail reason. Contains the message and arguments for message's numbered placeholders.
/// </summary>
/// <remarks>
/// In addition to the numbered <see cref="string.Format(string,object[])"/>-style placeholders, messages may contain a few
/// specialized placeholders as well. For instance, {reason} will be replaced with the reason of the assertion as passed
/// to BecauseOf. Other named placeholders will be replaced with
/// the <see cref="FluentAssertions.Execution.AssertionScope.Current"/> scope data passed through
/// <see cref="FluentAssertions.Execution.AssertionScope.AddNonReportable"/> and
/// <see cref="FluentAssertions.Execution.AssertionScope.AddReportable(string,string)"/>. Finally, a description of the
/// current subject can be passed through the {context:description} placeholder. This is used in the message if no
/// explicit context is specified through the <see cref="AssertionScope"/> constructor.
/// Note that only 10 arguments are supported in combination with a {reason}.
/// In addition to the numbered <see cref="string.Format(string,object[])"/>-style placeholders, messages may contain a
/// few specialized placeholders as well. For instance, <em>{reason}</em> will be replaced with the reason of the
/// assertion as passed to <see cref="AssertionScope.BecauseOf(string, object[])"/>.
/// <para>
/// Other named placeholders will be replaced with the <see cref="AssertionScope.Current"/> scope data passed through
/// <see cref="AssertionScope.AddNonReportable"/> and <see cref="AssertionScope.AddReportable(string,string)"/>.
/// </para>
/// <para>
/// Finally, a description of the current subject can be passed through the <em>{context:description}</em> placeholder.
/// This is used in the message if no explicit context is specified through the <see cref="AssertionScope"/> constructor.
/// </para>
/// <para>
/// Note that only 10 <c>args</c> are supported in combination with a <em>{reason}</em>.
/// </para>
/// </remarks>
public class FailReason
{
/// <summary>
/// Initializes a new instance of the <see cref="FailReason"/> class.
/// </summary>
/// <remarks>
/// <inheritdoc cref="FailReason"/>
/// </remarks>
public FailReason(string message, params object[] args)
{
Message = message;
Args = args;
}

/// <summary>
/// Message to be displayed in case of failed assertion. May contain
/// numbered <see cref="string.Format(string,object[])"/>-style placeholders as well
/// as specialized placeholders.
/// Message to be displayed in case of failed assertion. May contain numbered
/// <see cref="string.Format(string,object[])"/>-style placeholders as well as specialized placeholders.
/// </summary>
public string Message { get; }

/// <summary>
/// Arguments for the numbered <see cref="string.Format(string,object[])"/>-style placeholders
/// of <see cref="Message"/>.
/// Arguments for the numbered <see cref="string.Format(string,object[])"/>-style placeholders of <see cref="Message"/>.
/// </summary>
public object[] Args { get; }
}
Expand Down
Loading