From 211b77a5a6a235ed4a462ca4f2c20ce3e5235d81 Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Sun, 27 Aug 2023 20:04:52 +0200 Subject: [PATCH] Refactored parts of AssertionScope into AssertionChain and used that to improve the identifier in .Which constructs. --- .editorconfig | 5 + Build/Build.cs | 4 - Build/_build.csproj | 1 + FluentAssertions.sln.DotSettings | 14 +- Src/FluentAssertions/AndWhichConstraint.cs | 100 +- Src/FluentAssertions/AssertionExtensions.cs | 137 +- .../GenericCollectionAssertions.cs | 1099 +++++++++-------- .../GenericDictionaryAssertions.cs | 186 ++- .../Collections/StringCollectionAssertions.cs | 68 +- .../SubsequentOrderingAssertions.cs | 5 +- ...uentOrderingGenericCollectionAssertions.cs | 9 +- .../Common/MethodInfoExtensions.cs | 1 - .../Common/ObjectExtensions.cs | 9 + .../Common/StringExtensions.cs | 11 + .../CustomAssertionAttribute.cs | 2 +- .../CustomAssertionsAssemblyAttribute.cs | 2 +- .../EnumAssertionsExtensions.cs | 5 +- .../Equivalency/AssertionChainExtensions.cs | 19 + .../Equivalency/EquivalencyStep.cs | 2 +- .../EquivalencyValidationContext.cs | 2 +- .../Equivalency/EquivalencyValidator.cs | 31 +- .../Execution/CyclicReferenceDetector.cs | 13 +- .../Equivalency/IEquivalencyStep.cs | 3 +- .../Equivalency/IMemberMatchingRule.cs | 11 +- .../Matching/MappedMemberMatchingRule.cs | 3 +- .../Matching/MappedPathMatchingRule.cs | 3 +- .../Matching/MustMatchByNameRule.cs | 6 +- .../Matching/TryMatchByNameRule.cs | 3 +- .../MultiDimensionalArrayEquivalencyStep.cs | 31 +- Src/FluentAssertions/Equivalency/Node.cs | 11 +- .../Equivalency/Steps/AssertionResultSet.cs | 2 +- .../Steps/AssertionRuleEquivalencyStep.cs | 30 +- .../Steps/DictionaryEquivalencyStep.cs | 41 +- .../Steps/DictionaryInterfaceInfo.cs | 6 +- .../Equivalency/Steps/EnumEqualityStep.cs | 18 +- .../Steps/EnumerableEquivalencyStep.cs | 16 +- .../Steps/EnumerableEquivalencyValidator.cs | 31 +- ...numerableEquivalencyValidatorExtensions.cs | 54 +- .../Steps/EqualityComparerEquivalencyStep.cs | 3 +- .../Steps/GenericDictionaryEquivalencyStep.cs | 68 +- .../Steps/GenericEnumerableEquivalencyStep.cs | 18 +- .../Steps/SimpleEqualityEquivalencyStep.cs | 6 + .../Steps/StringEqualityEquivalencyStep.cs | 25 +- .../StructuralEqualityEquivalencyStep.cs | 14 +- .../Steps/ValueTypeEquivalencyStep.cs | 5 + .../Steps/XAttributeEquivalencyStep.cs | 8 +- .../Steps/XDocumentEquivalencyStep.cs | 8 +- .../Steps/XElementEquivalencyStep.cs | 8 +- .../EventRaisingExtensions.cs | 13 +- .../Events/EventAssertions.cs | 20 +- Src/FluentAssertions/Events/EventMonitor.cs | 3 +- .../ExceptionAssertionsExtensions.cs | 3 +- .../Execution/AssertionChain.cs | 321 +++++ .../Execution/AssertionScope.cs | 386 +----- .../Execution/ContextDataDictionary.cs | 9 +- .../Execution/Continuation.cs | 26 +- .../Execution/ContinuationOfGiven.cs | 17 +- .../Execution/ContinuedAssertionScope.cs | 156 --- Src/FluentAssertions/Execution/Execute.cs | 21 - Src/FluentAssertions/Execution/FailReason.cs | 6 +- ...eBuilder.cs => FailureMessageFormatter.cs} | 119 +- .../Execution/GivenSelector.cs | 68 +- .../Execution/IAssertionScope.cs | 186 --- .../Formatting/FormattedObjectGraph.cs | 19 +- Src/FluentAssertions/Formatting/Formatter.cs | 4 +- .../Formatting/PropertyInfoFormatter.cs | 9 +- .../Numeric/ByteAssertions.cs | 5 +- .../Numeric/ComparableTypeAssertions.cs | 34 +- .../Numeric/DecimalAssertions.cs | 5 +- .../Numeric/DoubleAssertions.cs | 5 +- .../Numeric/Int16Assertions.cs | 5 +- .../Numeric/Int32Assertions.cs | 5 +- .../Numeric/Int64Assertions.cs | 5 +- .../Numeric/NullableByteAssertions.cs | 5 +- .../Numeric/NullableDecimalAssertions.cs | 5 +- .../Numeric/NullableDoubleAssertions.cs | 5 +- .../Numeric/NullableInt16Assertions.cs | 5 +- .../Numeric/NullableInt32Assertions.cs | 5 +- .../Numeric/NullableInt64Assertions.cs | 5 +- .../Numeric/NullableNumericAssertions.cs | 17 +- .../Numeric/NullableSByteAssertions.cs | 5 +- .../Numeric/NullableSingleAssertions.cs | 5 +- .../Numeric/NullableUInt16Assertions.cs | 5 +- .../Numeric/NullableUInt32Assertions.cs | 5 +- .../Numeric/NullableUInt64Assertions.cs | 5 +- .../Numeric/NumericAssertions.cs | 45 +- .../Numeric/SByteAssertions.cs | 5 +- .../Numeric/SingleAssertions.cs | 5 +- .../Numeric/UInt16Assertions.cs | 5 +- .../Numeric/UInt32Assertions.cs | 5 +- .../Numeric/UInt64Assertions.cs | 5 +- .../NumericAssertionsExtensions.cs | 194 ++- .../ObjectAssertionsExtensions.cs | 5 +- Src/FluentAssertions/OccurrenceConstraint.cs | 5 +- .../Primitives/BooleanAssertions.cs | 38 +- .../Primitives/DateOnlyAssertions.cs | 136 +- .../Primitives/DateTimeAssertions.cs | 337 +++-- .../Primitives/DateTimeOffsetAssertions.cs | 388 +++--- .../DateTimeOffsetRangeAssertions.cs | 17 +- .../Primitives/DateTimeRangeAssertions.cs | 17 +- .../Primitives/EnumAssertions.cs | 79 +- .../Primitives/GuidAssertions.cs | 17 +- .../HttpResponseMessageAssertions.cs | 53 +- .../Primitives/IStringComparisonStrategy.cs | 2 +- .../Primitives/NullableBooleanAssertions.cs | 23 +- .../Primitives/NullableDateOnlyAssertions.cs | 15 +- .../Primitives/NullableDateTimeAssertions.cs | 15 +- .../NullableDateTimeOffsetAssertions.cs | 15 +- .../Primitives/NullableEnumAssertions.cs | 17 +- .../Primitives/NullableGuidAssertions.cs | 17 +- .../NullableSimpleTimeSpanAssertions.cs | 17 +- .../Primitives/NullableTimeOnlyAssertions.cs | 15 +- .../Primitives/ObjectAssertions.cs | 39 +- .../Primitives/ReferenceTypeAssertions.cs | 72 +- .../Primitives/SimpleTimeSpanAssertions.cs | 29 +- .../Primitives/StringAssertions.cs | 141 +-- .../Primitives/StringContainsStrategy.cs | 4 +- .../Primitives/StringEndStrategy.cs | 12 +- .../Primitives/StringEqualityStrategy.cs | 20 +- .../Primitives/StringStartStrategy.cs | 12 +- .../Primitives/StringValidator.cs | 14 +- .../StringValidatorSupportingNull.cs | 11 +- .../StringWildcardMatchingStrategy.cs | 6 +- .../Primitives/TimeOnlyAssertions.cs | 176 ++- .../Specialized/ActionAssertions.cs | 22 +- .../Specialized/AsyncFunctionAssertions.cs | 71 +- .../Specialized/DelegateAssertions.cs | 34 +- .../Specialized/DelegateAssertionsBase.cs | 40 +- .../Specialized/ExceptionAssertions.cs | 104 +- .../Specialized/ExecutionTimeAssertions.cs | 14 +- .../Specialized/FunctionAssertionHelpers.cs | 57 - .../Specialized/FunctionAssertions.cs | 71 +- .../GenericAsyncFunctionAssertions.cs | 41 +- .../NonGenericAsyncFunctionAssertions.cs | 32 +- .../TaskCompletionSourceAssertions.cs | 44 +- .../Streams/BufferedStreamAssertions.cs | 38 +- .../Streams/StreamAssertions.cs | 103 +- .../Types/AssemblyAssertions.cs | 57 +- .../Types/ConstructorInfoAssertions.cs | 8 +- .../Types/MemberInfoAssertions.cs | 21 +- .../Types/MethodBaseAssertions.cs | 34 +- .../Types/MethodInfoAssertions.cs | 57 +- .../Types/MethodInfoSelectorAssertions.cs | 21 +- .../Types/PropertyInfoAssertions.cs | 215 ++-- .../Types/PropertyInfoSelectorAssertions.cs | 20 +- Src/FluentAssertions/Types/TypeAssertions.cs | 378 +++--- .../Types/TypeSelectorAssertions.cs | 33 +- .../Xml/Equivalency/XmlReaderValidator.cs | 12 +- .../Xml/XAttributeAssertions.cs | 17 +- .../Xml/XDocumentAssertions.cs | 39 +- .../Xml/XElementAssertions.cs | 47 +- .../Xml/XmlElementAssertions.cs | 19 +- Src/FluentAssertions/Xml/XmlNodeAssertions.cs | 16 +- .../XmlAssertionExtensions.cs | 5 +- .../FluentAssertions/net47.verified.txt | 322 +++-- .../FluentAssertions/net6.0.verified.txt | 335 +++-- .../netstandard2.0.verified.txt | 320 +++-- .../netstandard2.1.verified.txt | 315 +++-- Tests/Benchmarks/Program.cs | 1 - .../BasicSpecs.cs | 3 +- .../CollectionSpecs.cs | 3 +- .../DictionarySpecs.cs | 23 + .../ExtensibilitySpecs.cs | 10 +- .../AssertionExtensions.cs | 13 +- .../AssertionExtensionsSpecs.cs | 39 +- .../AssertionFailureSpecs.cs | 17 +- .../AssertionOptionsSpecs.cs | 2 +- ...CollectionAssertionSpecs.BeEquivalentTo.cs | 6 +- .../CollectionAssertionSpecs.Contain.cs | 21 +- ...ctionAssertionSpecs.ContainEquivalentOf.cs | 31 +- ...CollectionAssertionSpecs.ContainInOrder.cs | 5 +- .../CollectionAssertionSpecs.ContainSingle.cs | 34 +- .../CollectionAssertionSpecs.HaveCount.cs | 5 +- .../CollectionAssertionSpecs.HaveElementAt.cs | 14 + ...ctionAssertionSpecs.OnlyHaveUniqueItems.cs | 4 +- .../Collections/CollectionAssertionSpecs.cs | 19 +- ...tionAssertionOfStringSpecs.ContainMatch.cs | 16 +- ...icDictionaryAssertionSpecs.ContainValue.cs | 41 +- ...cDictionaryAssertionSpecs.ContainValues.cs | 19 +- .../FunctionExceptionAssertionSpecs.cs | 32 +- .../Execution/AssertionChainSpecs.Chaining.cs | 599 +++++++++ .../AssertionChainSpecs.MessageFormating.cs | 390 ++++++ .../AssertionScope.ChainingApiSpecs.cs | 588 --------- .../AssertionScope.ContextDataSpecs.cs | 68 - .../AssertionScope.MessageFormatingSpecs.cs | 522 -------- ...> AssertionScopeSpecs.ScopedFormatters.cs} | 0 .../Execution/AssertionScopeSpecs.cs | 64 +- ...rSpecs.cs => CallerIdentificationSpecs.cs} | 43 +- .../Execution/GivenSelectorSpecs.cs | 82 +- .../Formatting/FormatterSpecs.cs | 86 +- .../OccurrenceConstraintSpecs.cs | 4 +- .../Primitives/ObjectAssertionSpecs.cs | 3 +- .../ReferenceTypeAssertionsSpecs.cs | 8 +- .../SimpleTimeSpanAssertionSpecs.cs | 3 +- .../Primitives/StringComparisonSpecs.cs | 8 +- .../Specialized/AssemblyAssertionSpecs.cs | 18 +- .../ExecutionTimeAssertionsSpecs.cs | 3 +- .../TaskCompletionSourceAssertionSpecs.cs | 2 +- .../Specialized/TaskOfTAssertionSpecs.cs | 50 +- .../Types/MethodBaseAssertionSpecs.cs | 20 +- .../Types/PropertyInfoAssertionSpecs.cs | 68 +- .../PropertyInfoSelectorAssertionSpecs.cs | 23 +- ...ionSpecs.HaveExplicitConversionOperator.cs | 28 +- ...ionSpecs.HaveImplicitConversionOperator.cs | 19 + .../Types/TypeAssertionSpecs.HaveProperty.cs | 33 +- .../Xml/XDocumentAssertionSpecs.cs | 37 + qodana.yaml | 1 + 207 files changed, 5897 insertions(+), 5900 deletions(-) create mode 100644 Src/FluentAssertions/Equivalency/AssertionChainExtensions.cs create mode 100644 Src/FluentAssertions/Execution/AssertionChain.cs delete mode 100644 Src/FluentAssertions/Execution/ContinuedAssertionScope.cs delete mode 100644 Src/FluentAssertions/Execution/Execute.cs rename Src/FluentAssertions/Execution/{MessageBuilder.cs => FailureMessageFormatter.cs} (68%) delete mode 100644 Src/FluentAssertions/Execution/IAssertionScope.cs delete mode 100644 Src/FluentAssertions/Specialized/FunctionAssertionHelpers.cs create mode 100644 Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.Chaining.cs create mode 100644 Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.MessageFormating.cs delete mode 100644 Tests/FluentAssertions.Specs/Execution/AssertionScope.ChainingApiSpecs.cs delete mode 100644 Tests/FluentAssertions.Specs/Execution/AssertionScope.ContextDataSpecs.cs delete mode 100644 Tests/FluentAssertions.Specs/Execution/AssertionScope.MessageFormatingSpecs.cs rename Tests/FluentAssertions.Specs/Execution/{AssertionScope.ScopedFormatters.cs => AssertionScopeSpecs.ScopedFormatters.cs} (100%) rename Tests/FluentAssertions.Specs/Execution/{CallerIdentifierSpecs.cs => CallerIdentificationSpecs.cs} (93%) diff --git a/.editorconfig b/.editorconfig index f4874fecc7..0ea4ede3a9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -199,6 +199,11 @@ dotnet_diagnostic.SA1116.severity = none dotnet_diagnostic.SA1117.severity = none # SA1200: Using directive should appear within a namespace declaration dotnet_diagnostic.SA1200.severity = none + +# Purpose: Use string. Empty for empty strings +# Reason: There's no performance difference. See https://medium.com/@dk.kravtsov/string-empty-vs-in-c-70c64971161f +dotnet_diagnostic.SA1122.severity = none + # SA1124: Do not use regions dotnet_diagnostic.SA1124.severity = none # SA1201: A property should not follow a method diff --git a/Build/Build.cs b/Build/Build.cs index 1859443ae5..a3f1c2695b 100644 --- a/Build/Build.cs +++ b/Build/Build.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Net.Http; -using System.Runtime.InteropServices; using LibGit2Sharp; -using Microsoft.Build.Tasks; using Nuke.Common; using Nuke.Common.CI.GitHubActions; using Nuke.Common.Execution; diff --git a/Build/_build.csproj b/Build/_build.csproj index e3711ab625..91b5c3e46d 100644 --- a/Build/_build.csproj +++ b/Build/_build.csproj @@ -7,6 +7,7 @@ ..\ ..\ 8.1.0 + 1 OS_WINDOWS diff --git a/FluentAssertions.sln.DotSettings b/FluentAssertions.sln.DotSettings index e58cf3d554..785299fbe3 100644 --- a/FluentAssertions.sln.DotSettings +++ b/FluentAssertions.sln.DotSettings @@ -1,4 +1,4 @@ - + True True False @@ -143,7 +143,7 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> - + OUTLINE SOLUTION_FOLDER True @@ -161,17 +161,17 @@ 4 False True - True - 1 - True - 0 + False + + False + aaa Arrange-Act-Assert [Fact] public void $END$() { // Arrange - + // Act diff --git a/Src/FluentAssertions/AndWhichConstraint.cs b/Src/FluentAssertions/AndWhichConstraint.cs index a30126ba2f..b15aaaf5c8 100644 --- a/Src/FluentAssertions/AndWhichConstraint.cs +++ b/Src/FluentAssertions/AndWhichConstraint.cs @@ -2,45 +2,84 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions.Common; +using FluentAssertions.Execution; using FluentAssertions.Formatting; namespace FluentAssertions; /// -/// Constraint which can be returned from an assertion which matches a condition and which will allow -/// further matches to be performed on the matched condition as well as the parent constraint. +/// Provides a property that can be used in chained assertions where the prior assertions returns a +/// single object that the assertion continues on. /// -/// The type of the original constraint that was matched -/// The type of the matched object which the parent constraint matched -public class AndWhichConstraint : AndConstraint +public class AndWhichConstraint : AndConstraint { - private readonly Lazy matchedConstraint; + private readonly AssertionChain assertionChain; + private readonly string pathPostfix; + private readonly Lazy getSubject; - public AndWhichConstraint(TParentConstraint parentConstraint, TMatchedElement matchedConstraint) - : base(parentConstraint) + /// + /// Creates an object that allows continuing an assertion executed through and + /// which resulted in a single . + /// + public AndWhichConstraint(TParent parent, TSubject subject) + : base(parent) + { + getSubject = new Lazy(() => subject); + } + + /// + /// Creates an object that allows continuing an assertion executed through and + /// which resulted in a single on an existing , but where + /// the previous caller identifier is post-fixed with . + /// + public AndWhichConstraint(TParent parent, TSubject subject, AssertionChain assertionChain, string pathPostfix = "") + : base(parent) { - this.matchedConstraint = - new Lazy(() => matchedConstraint); + getSubject = new Lazy(() => subject); + + this.assertionChain = assertionChain; + this.pathPostfix = pathPostfix; } - public AndWhichConstraint(TParentConstraint parentConstraint, IEnumerable matchedConstraint) - : base(parentConstraint) + /// + /// Creates an object that allows continuing an assertion executed through and + /// which resulted in a potential collection of objects through . + /// + /// + /// If contains more than one object, a clear exception is thrown. + /// + public AndWhichConstraint(TParent parent, IEnumerable subjects) + : base(parent) { - this.matchedConstraint = - new Lazy( - () => SingleOrDefault(matchedConstraint)); + getSubject = new Lazy(() => SingleOrDefault(subjects)); } - private static TMatchedElement SingleOrDefault( - IEnumerable matchedConstraint) + /// + /// Creates an object that allows continuing an assertion executed through and + /// which resulted in a potential collection of objects through on an + /// existing , but where + /// the previous caller identifier is post-fixed with . + /// + /// + /// If contains more than one object, a clear exception is thrown. + /// + public AndWhichConstraint(TParent parent, IEnumerable subjects, AssertionChain assertionChain, string pathPostfix) + : base(parent) { - TMatchedElement[] matchedElements = matchedConstraint.ToArray(); + getSubject = new Lazy(() => SingleOrDefault(subjects)); + + this.assertionChain = assertionChain; + this.pathPostfix = pathPostfix; + } + + private static TSubject SingleOrDefault(IEnumerable subjects) + { + TSubject[] matchedElements = subjects.ToArray(); if (matchedElements.Length > 1) { string foundObjects = string.Join(Environment.NewLine, - matchedElements.Select( - ele => "\t" + Formatter.ToString(ele))); + matchedElements.Select(ele => "\t" + Formatter.ToString(ele))); string message = "More than one object found. FluentAssertions cannot determine which object is meant." + $" Found objects:{Environment.NewLine}{foundObjects}"; @@ -54,13 +93,24 @@ private static TMatchedElement SingleOrDefault( /// /// Returns the single result of a prior assertion that is used to select a nested or collection item. /// - public TMatchedElement Which => matchedConstraint.Value; + /// + /// Just a convenience property that returns the same value as . + /// + public TSubject Subject => Which; /// /// Returns the single result of a prior assertion that is used to select a nested or collection item. /// - /// - /// Just a convenience property that returns the same value as . - /// - public TMatchedElement Subject => Which; + public TSubject Which + { + get + { + if (pathPostfix is not null and not "") + { + assertionChain.WithCallerPostfix(pathPostfix).ReuseOnce(); + } + + return getSubject.Value; + } + } } diff --git a/Src/FluentAssertions/AssertionExtensions.cs b/Src/FluentAssertions/AssertionExtensions.cs index 0c69d58c41..47630b6d65 100644 --- a/Src/FluentAssertions/AssertionExtensions.cs +++ b/Src/FluentAssertions/AssertionExtensions.cs @@ -11,6 +11,7 @@ using System.Xml.Linq; using FluentAssertions.Collections; using FluentAssertions.Common; +using FluentAssertions.Execution; using FluentAssertions.Numeric; using FluentAssertions.Primitives; using FluentAssertions.Specialized; @@ -149,7 +150,6 @@ public static ExecutionTime ExecutionTime(this Action action, StartTimer createT /// /// Provides methods for asserting the execution time of an async action. /// - /// An async action to measure the execution time of. /// /// Returns an object for asserting that the execution time matches certain conditions. /// @@ -167,7 +167,7 @@ public static ExecutionTime ExecutionTime(this Func action) [Pure] public static ExecutionTimeAssertions Should(this ExecutionTime executionTime) { - return new ExecutionTimeAssertions(executionTime); + return new ExecutionTimeAssertions(executionTime, AssertionChain.GetOrCreate()); } /// @@ -177,7 +177,7 @@ public static ExecutionTimeAssertions Should(this ExecutionTime executionTime) [Pure] public static AssemblyAssertions Should([NotNull] this Assembly assembly) { - return new AssemblyAssertions(assembly); + return new AssemblyAssertions(assembly, AssertionChain.GetOrCreate()); } /// @@ -187,7 +187,7 @@ public static AssemblyAssertions Should([NotNull] this Assembly assembly) [Pure] public static XDocumentAssertions Should([NotNull] this XDocument actualValue) { - return new XDocumentAssertions(actualValue); + return new XDocumentAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -197,7 +197,7 @@ public static XDocumentAssertions Should([NotNull] this XDocument actualValue) [Pure] public static XElementAssertions Should([NotNull] this XElement actualValue) { - return new XElementAssertions(actualValue); + return new XElementAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -207,7 +207,7 @@ public static XElementAssertions Should([NotNull] this XElement actualValue) [Pure] public static XAttributeAssertions Should([NotNull] this XAttribute actualValue) { - return new XAttributeAssertions(actualValue); + return new XAttributeAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -217,9 +217,11 @@ public static XAttributeAssertions Should([NotNull] this XAttribute actualValue) [Pure] public static StreamAssertions Should([NotNull] this Stream actualValue) { - return new StreamAssertions(actualValue); + return new StreamAssertions(actualValue, AssertionChain.GetOrCreate()); } +#if NET6_0_OR_GREATER || NETSTANDARD2_1 + /// /// Returns an object that can be used to assert the /// current . @@ -227,9 +229,11 @@ public static StreamAssertions Should([NotNull] this Stream actualValue) [Pure] public static BufferedStreamAssertions Should([NotNull] this BufferedStream actualValue) { - return new BufferedStreamAssertions(actualValue); + return new BufferedStreamAssertions(actualValue, AssertionChain.GetOrCreate()); } +#endif + /// /// Forces enumerating a collection. Should be used to assert that a method that uses the /// keyword throws a particular exception. @@ -284,7 +288,7 @@ private static void ForceEnumeration(T subject, Func enumerab [Pure] public static ObjectAssertions Should([NotNull] this object actualValue) { - return new ObjectAssertions(actualValue); + return new ObjectAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -294,7 +298,7 @@ public static ObjectAssertions Should([NotNull] this object actualValue) [Pure] public static BooleanAssertions Should(this bool actualValue) { - return new BooleanAssertions(actualValue); + return new BooleanAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -304,7 +308,7 @@ public static BooleanAssertions Should(this bool actualValue) [Pure] public static NullableBooleanAssertions Should(this bool? actualValue) { - return new NullableBooleanAssertions(actualValue); + return new NullableBooleanAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -314,7 +318,7 @@ public static NullableBooleanAssertions Should(this bool? actualValue) [Pure] public static HttpResponseMessageAssertions Should([NotNull] this HttpResponseMessage actualValue) { - return new HttpResponseMessageAssertions(actualValue); + return new HttpResponseMessageAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -324,7 +328,7 @@ public static HttpResponseMessageAssertions Should([NotNull] this HttpResponseMe [Pure] public static GuidAssertions Should(this Guid actualValue) { - return new GuidAssertions(actualValue); + return new GuidAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -334,7 +338,7 @@ public static GuidAssertions Should(this Guid actualValue) [Pure] public static NullableGuidAssertions Should(this Guid? actualValue) { - return new NullableGuidAssertions(actualValue); + return new NullableGuidAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -344,7 +348,7 @@ public static NullableGuidAssertions Should(this Guid? actualValue) [Pure] public static GenericCollectionAssertions Should([NotNull] this IEnumerable actualValue) { - return new GenericCollectionAssertions(actualValue); + return new GenericCollectionAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -354,7 +358,7 @@ public static GenericCollectionAssertions Should([NotNull] this IEnumerabl [Pure] public static StringCollectionAssertions Should([NotNull] this IEnumerable @this) { - return new StringCollectionAssertions(@this); + return new StringCollectionAssertions(@this, AssertionChain.GetOrCreate()); } /// @@ -365,7 +369,7 @@ public static StringCollectionAssertions Should([NotNull] this IEnumerable, TKey, TValue> Should( [NotNull] this IDictionary actualValue) { - return new GenericDictionaryAssertions, TKey, TValue>(actualValue); + return new GenericDictionaryAssertions, TKey, TValue>(actualValue, AssertionChain.GetOrCreate()); } /// @@ -376,7 +380,8 @@ public static GenericDictionaryAssertions, TKey, TValu public static GenericDictionaryAssertions>, TKey, TValue> Should( [NotNull] this IEnumerable> actualValue) { - return new GenericDictionaryAssertions>, TKey, TValue>(actualValue); + return new GenericDictionaryAssertions>, TKey, TValue>(actualValue, + AssertionChain.GetOrCreate()); } /// @@ -388,7 +393,7 @@ public static GenericDictionaryAssertions Should> { - return new GenericDictionaryAssertions(actualValue); + return new GenericDictionaryAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -398,7 +403,7 @@ public static GenericDictionaryAssertions Should @@ -408,7 +413,7 @@ public static DateTimeAssertions Should(this DateTime actualValue) [Pure] public static DateTimeOffsetAssertions Should(this DateTimeOffset actualValue) { - return new DateTimeOffsetAssertions(actualValue); + return new DateTimeOffsetAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -418,7 +423,7 @@ public static DateTimeOffsetAssertions Should(this DateTimeOffset actualValue) [Pure] public static NullableDateTimeAssertions Should(this DateTime? actualValue) { - return new NullableDateTimeAssertions(actualValue); + return new NullableDateTimeAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -428,7 +433,7 @@ public static NullableDateTimeAssertions Should(this DateTime? actualValue) [Pure] public static NullableDateTimeOffsetAssertions Should(this DateTimeOffset? actualValue) { - return new NullableDateTimeOffsetAssertions(actualValue); + return new NullableDateTimeOffsetAssertions(actualValue, AssertionChain.GetOrCreate()); } #if NET6_0_OR_GREATER @@ -439,7 +444,7 @@ public static NullableDateTimeOffsetAssertions Should(this DateTimeOffset? actua [Pure] public static DateOnlyAssertions Should(this DateOnly actualValue) { - return new DateOnlyAssertions(actualValue); + return new DateOnlyAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -449,7 +454,7 @@ public static DateOnlyAssertions Should(this DateOnly actualValue) [Pure] public static NullableDateOnlyAssertions Should(this DateOnly? actualValue) { - return new NullableDateOnlyAssertions(actualValue); + return new NullableDateOnlyAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -459,7 +464,7 @@ public static NullableDateOnlyAssertions Should(this DateOnly? actualValue) [Pure] public static TimeOnlyAssertions Should(this TimeOnly actualValue) { - return new TimeOnlyAssertions(actualValue); + return new TimeOnlyAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -469,7 +474,7 @@ public static TimeOnlyAssertions Should(this TimeOnly actualValue) [Pure] public static NullableTimeOnlyAssertions Should(this TimeOnly? actualValue) { - return new NullableTimeOnlyAssertions(actualValue); + return new NullableTimeOnlyAssertions(actualValue, AssertionChain.GetOrCreate()); } #endif @@ -481,7 +486,7 @@ public static NullableTimeOnlyAssertions Should(this TimeOnly? actualValue) [Pure] public static ComparableTypeAssertions Should([NotNull] this IComparable comparableValue) { - return new ComparableTypeAssertions(comparableValue); + return new ComparableTypeAssertions(comparableValue, AssertionChain.GetOrCreate()); } /// @@ -491,7 +496,7 @@ public static ComparableTypeAssertions Should([NotNull] this IComparable Should(this int actualValue) { - return new Int32Assertions(actualValue); + return new Int32Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -501,7 +506,7 @@ public static NumericAssertions Should(this int actualValue) [Pure] public static NullableNumericAssertions Should(this int? actualValue) { - return new NullableInt32Assertions(actualValue); + return new NullableInt32Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -511,7 +516,7 @@ public static NullableNumericAssertions Should(this int? actualValue) [Pure] public static NumericAssertions Should(this uint actualValue) { - return new UInt32Assertions(actualValue); + return new UInt32Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -521,7 +526,7 @@ public static NumericAssertions Should(this uint actualValue) [Pure] public static NullableNumericAssertions Should(this uint? actualValue) { - return new NullableUInt32Assertions(actualValue); + return new NullableUInt32Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -531,7 +536,7 @@ public static NullableNumericAssertions Should(this uint? actualValue) [Pure] public static NumericAssertions Should(this decimal actualValue) { - return new DecimalAssertions(actualValue); + return new DecimalAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -541,7 +546,7 @@ public static NumericAssertions Should(this decimal actualValue) [Pure] public static NullableNumericAssertions Should(this decimal? actualValue) { - return new NullableDecimalAssertions(actualValue); + return new NullableDecimalAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -551,7 +556,7 @@ public static NullableNumericAssertions Should(this decimal? actualValu [Pure] public static NumericAssertions Should(this byte actualValue) { - return new ByteAssertions(actualValue); + return new ByteAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -561,7 +566,7 @@ public static NumericAssertions Should(this byte actualValue) [Pure] public static NullableNumericAssertions Should(this byte? actualValue) { - return new NullableByteAssertions(actualValue); + return new NullableByteAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -571,7 +576,7 @@ public static NullableNumericAssertions Should(this byte? actualValue) [Pure] public static NumericAssertions Should(this sbyte actualValue) { - return new SByteAssertions(actualValue); + return new SByteAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -581,7 +586,7 @@ public static NumericAssertions Should(this sbyte actualValue) [Pure] public static NullableNumericAssertions Should(this sbyte? actualValue) { - return new NullableSByteAssertions(actualValue); + return new NullableSByteAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -591,7 +596,7 @@ public static NullableNumericAssertions Should(this sbyte? actualValue) [Pure] public static NumericAssertions Should(this short actualValue) { - return new Int16Assertions(actualValue); + return new Int16Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -601,7 +606,7 @@ public static NumericAssertions Should(this short actualValue) [Pure] public static NullableNumericAssertions Should(this short? actualValue) { - return new NullableInt16Assertions(actualValue); + return new NullableInt16Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -611,7 +616,7 @@ public static NullableNumericAssertions Should(this short? actualValue) [Pure] public static NumericAssertions Should(this ushort actualValue) { - return new UInt16Assertions(actualValue); + return new UInt16Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -621,7 +626,7 @@ public static NumericAssertions Should(this ushort actualValue) [Pure] public static NullableNumericAssertions Should(this ushort? actualValue) { - return new NullableUInt16Assertions(actualValue); + return new NullableUInt16Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -631,7 +636,7 @@ public static NullableNumericAssertions Should(this ushort? actualValue) [Pure] public static NumericAssertions Should(this long actualValue) { - return new Int64Assertions(actualValue); + return new Int64Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -641,7 +646,7 @@ public static NumericAssertions Should(this long actualValue) [Pure] public static NullableNumericAssertions Should(this long? actualValue) { - return new NullableInt64Assertions(actualValue); + return new NullableInt64Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -651,7 +656,7 @@ public static NullableNumericAssertions Should(this long? actualValue) [Pure] public static NumericAssertions Should(this ulong actualValue) { - return new UInt64Assertions(actualValue); + return new UInt64Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -661,7 +666,7 @@ public static NumericAssertions Should(this ulong actualValue) [Pure] public static NullableNumericAssertions Should(this ulong? actualValue) { - return new NullableUInt64Assertions(actualValue); + return new NullableUInt64Assertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -671,7 +676,7 @@ public static NullableNumericAssertions Should(this ulong? actualValue) [Pure] public static NumericAssertions Should(this float actualValue) { - return new SingleAssertions(actualValue); + return new SingleAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -681,7 +686,7 @@ public static NumericAssertions Should(this float actualValue) [Pure] public static NullableNumericAssertions Should(this float? actualValue) { - return new NullableSingleAssertions(actualValue); + return new NullableSingleAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -691,7 +696,7 @@ public static NullableNumericAssertions Should(this float? actualValue) [Pure] public static NumericAssertions Should(this double actualValue) { - return new DoubleAssertions(actualValue); + return new DoubleAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -701,7 +706,7 @@ public static NumericAssertions Should(this double actualValue) [Pure] public static NullableNumericAssertions Should(this double? actualValue) { - return new NullableDoubleAssertions(actualValue); + return new NullableDoubleAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -711,7 +716,7 @@ public static NullableNumericAssertions Should(this double? actualValue) [Pure] public static StringAssertions Should([NotNull] this string actualValue) { - return new StringAssertions(actualValue); + return new StringAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -721,7 +726,7 @@ public static StringAssertions Should([NotNull] this string actualValue) [Pure] public static SimpleTimeSpanAssertions Should(this TimeSpan actualValue) { - return new SimpleTimeSpanAssertions(actualValue); + return new SimpleTimeSpanAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -731,7 +736,7 @@ public static SimpleTimeSpanAssertions Should(this TimeSpan actualValue) [Pure] public static NullableSimpleTimeSpanAssertions Should(this TimeSpan? actualValue) { - return new NullableSimpleTimeSpanAssertions(actualValue); + return new NullableSimpleTimeSpanAssertions(actualValue, AssertionChain.GetOrCreate()); } /// @@ -741,7 +746,7 @@ public static NullableSimpleTimeSpanAssertions Should(this TimeSpan? actualValue [Pure] public static TypeAssertions Should([NotNull] this Type subject) { - return new TypeAssertions(subject); + return new TypeAssertions(subject, AssertionChain.GetOrCreate()); } /// @@ -754,7 +759,7 @@ public static TypeSelectorAssertions Should(this TypeSelector typeSelector) { Guard.ThrowIfArgumentIsNull(typeSelector); - return new TypeSelectorAssertions(typeSelector.ToArray()); + return new TypeSelectorAssertions(AssertionChain.GetOrCreate(), typeSelector.ToArray()); } /// @@ -765,7 +770,7 @@ public static TypeSelectorAssertions Should(this TypeSelector typeSelector) [Pure] public static ConstructorInfoAssertions Should([NotNull] this ConstructorInfo constructorInfo) { - return new ConstructorInfoAssertions(constructorInfo); + return new ConstructorInfoAssertions(constructorInfo, AssertionChain.GetOrCreate()); } /// @@ -775,7 +780,7 @@ public static ConstructorInfoAssertions Should([NotNull] this ConstructorInfo co [Pure] public static MethodInfoAssertions Should([NotNull] this MethodInfo methodInfo) { - return new MethodInfoAssertions(methodInfo); + return new MethodInfoAssertions(methodInfo, AssertionChain.GetOrCreate()); } /// @@ -789,7 +794,7 @@ public static MethodInfoSelectorAssertions Should(this MethodInfoSelector method { Guard.ThrowIfArgumentIsNull(methodSelector); - return new MethodInfoSelectorAssertions(methodSelector.ToArray()); + return new MethodInfoSelectorAssertions(AssertionChain.GetOrCreate(), methodSelector.ToArray()); } /// @@ -800,7 +805,7 @@ public static MethodInfoSelectorAssertions Should(this MethodInfoSelector method [Pure] public static PropertyInfoAssertions Should([NotNull] this PropertyInfo propertyInfo) { - return new PropertyInfoAssertions(propertyInfo); + return new PropertyInfoAssertions(propertyInfo, AssertionChain.GetOrCreate()); } /// @@ -814,7 +819,7 @@ public static PropertyInfoSelectorAssertions Should(this PropertyInfoSelector pr { Guard.ThrowIfArgumentIsNull(propertyInfoSelector); - return new PropertyInfoSelectorAssertions(propertyInfoSelector.ToArray()); + return new PropertyInfoSelectorAssertions(AssertionChain.GetOrCreate(), propertyInfoSelector.ToArray()); } /// @@ -824,7 +829,7 @@ public static PropertyInfoSelectorAssertions Should(this PropertyInfoSelector pr [Pure] public static ActionAssertions Should([NotNull] this Action action) { - return new ActionAssertions(action, Extractor); + return new ActionAssertions(action, Extractor, AssertionChain.GetOrCreate()); } /// @@ -834,7 +839,7 @@ public static ActionAssertions Should([NotNull] this Action action) [Pure] public static NonGenericAsyncFunctionAssertions Should([NotNull] this Func action) { - return new NonGenericAsyncFunctionAssertions(action, Extractor); + return new NonGenericAsyncFunctionAssertions(action, Extractor, AssertionChain.GetOrCreate()); } /// @@ -844,7 +849,7 @@ public static NonGenericAsyncFunctionAssertions Should([NotNull] this Func [Pure] public static GenericAsyncFunctionAssertions Should([NotNull] this Func> action) { - return new GenericAsyncFunctionAssertions(action, Extractor); + return new GenericAsyncFunctionAssertions(action, Extractor, AssertionChain.GetOrCreate()); } /// @@ -854,7 +859,7 @@ public static GenericAsyncFunctionAssertions Should([NotNull] this Func Should([NotNull] this Func func) { - return new FunctionAssertions(func, Extractor); + return new FunctionAssertions(func, Extractor, AssertionChain.GetOrCreate()); } /// @@ -864,7 +869,7 @@ public static FunctionAssertions Should([NotNull] this Func func) [Pure] public static TaskCompletionSourceAssertions Should(this TaskCompletionSource tcs) { - return new TaskCompletionSourceAssertions(tcs); + return new TaskCompletionSourceAssertions(tcs, AssertionChain.GetOrCreate()); } #if !NETSTANDARD2_0 @@ -906,7 +911,7 @@ public static IMonitor Monitor(this T eventSource, Action : GenericCollectionAssertions, T, GenericCollectionAssertions> { - public GenericCollectionAssertions(IEnumerable actualValue) - : base(actualValue) + public GenericCollectionAssertions(IEnumerable actualValue, AssertionChain assertionChain) + : base(actualValue, assertionChain) { } } @@ -28,22 +28,26 @@ public class GenericCollectionAssertions : GenericCollectionAssertions> where TCollection : IEnumerable { - public GenericCollectionAssertions(TCollection actualValue) - : base(actualValue) + public GenericCollectionAssertions(TCollection actualValue, AssertionChain assertionChain) + : base(actualValue, assertionChain) { } } #pragma warning disable CS0659, S1206 // Ignore not overriding Object.GetHashCode() #pragma warning disable CA1065 // Ignore throwing NotSupportedException from Equals + [DebuggerNonUserCode] public class GenericCollectionAssertions : ReferenceTypeAssertions where TCollection : IEnumerable where TAssertions : GenericCollectionAssertions { - public GenericCollectionAssertions(TCollection actualValue) - : base(actualValue) + private readonly AssertionChain assertionChain; + + public GenericCollectionAssertions(TCollection actualValue, AssertionChain assertionChain) + : base(actualValue, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -63,9 +67,10 @@ public GenericCollectionAssertions(TCollection actualValue) /// Zero or more objects to format using the placeholders in . /// public AndWhichConstraint> AllBeAssignableTo( - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type to be {0}{reason}, but found {context:the collection} is .", @@ -73,18 +78,16 @@ public AndWhichConstraint> AllBeAssignabl IEnumerable matches = []; - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type to be {0}{reason}, ", typeof(TExpectation).FullName) - .ForCondition(Subject!.All(x => x is not null)) - .FailWith("but found a null element.") - .Then - .ForCondition(Subject.All(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", () => $"[{string.Join(", ", Subject.Select(x => GetType(x).FullName))}]") - .Then - .ClearExpectation(); + .WithExpectation("Expected type to be {0}{reason}, ", typeof(TExpectation).FullName, chain => chain + .ForCondition(Subject!.All(x => x is not null)) + .FailWith("but found a null element.") + .Then + .ForCondition(Subject.All(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) + .FailWith("but found {0}.", () => $"[{string.Join(", ", Subject.Select(x => GetType(x).FullName))}]")); matches = Subject.OfType(); } @@ -109,20 +112,18 @@ public AndConstraint AllBeAssignableTo(Type expectedType, { Guard.ThrowIfArgumentIsNull(expectedType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type to be {0}{reason}, ", expectedType.FullName) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found {context:collection} is .") - .Then - .ForCondition(subject => subject.All(x => x is not null)) - .FailWith("but found a null element.") - .Then - .ForCondition(subject => subject.All(x => expectedType.IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", subject => $"[{string.Join(", ", subject.Select(x => GetType(x).FullName))}]") - .Then - .ClearExpectation(); + .WithExpectation("Expected type to be {0}{reason}, ", expectedType.FullName, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found {context:collection} is .") + .Then + .ForCondition(subject => subject.All(x => x is not null)) + .FailWith("but found a null element.") + .Then + .ForCondition(subject => subject.All(x => expectedType.IsAssignableFrom(GetType(x)))) + .FailWith("but found {0}.", subject => $"[{string.Join(", ", subject.Select(x => GetType(x).FullName))}]")); return new AndConstraint((TAssertions)this); } @@ -180,7 +181,8 @@ public AndConstraint AllBeEquivalentTo(TExpectation e /// is . public AndConstraint AllBeEquivalentTo(TExpectation expectation, Func, EquivalencyOptions> config, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(config); @@ -210,9 +212,10 @@ public AndConstraint AllBeEquivalentTo(TExpectation e /// Zero or more objects to format using the placeholders in . /// public AndWhichConstraint> AllBeOfType( - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type to be {0}{reason}, but found {context:collection} is .", @@ -220,18 +223,16 @@ public AndWhichConstraint> AllBeOfType matches = []; - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type to be {0}{reason}, ", typeof(TExpectation).FullName) - .ForCondition(Subject!.All(x => x is not null)) - .FailWith("but found a null element.") - .Then - .ForCondition(Subject.All(x => typeof(TExpectation) == GetType(x))) - .FailWith("but found {0}.", () => $"[{string.Join(", ", Subject.Select(x => GetType(x).FullName))}]") - .Then - .ClearExpectation(); + .WithExpectation("Expected type to be {0}{reason}, ", typeof(TExpectation).FullName, chain => chain + .ForCondition(Subject!.All(x => x is not null)) + .FailWith("but found a null element.") + .Then + .ForCondition(Subject.All(x => typeof(TExpectation) == GetType(x))) + .FailWith("but found {0}.", () => $"[{string.Join(", ", Subject.Select(x => GetType(x).FullName))}]")); matches = Subject.OfType(); } @@ -251,25 +252,23 @@ public AndWhichConstraint> AllBeOfType. /// /// is . - public AndConstraint AllBeOfType(Type expectedType, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint AllBeOfType(Type expectedType, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expectedType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type to be {0}{reason}, ", expectedType.FullName) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found {context:collection} is .") - .Then - .ForCondition(subject => subject.All(x => x is not null)) - .FailWith("but found a null element.") - .Then - .ForCondition(subject => subject.All(x => expectedType == GetType(x))) - .FailWith("but found {0}.", subject => $"[{string.Join(", ", subject.Select(x => GetType(x).FullName))}]") - .Then - .ClearExpectation(); + .WithExpectation("Expected type to be {0}{reason}, ", expectedType.FullName, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found {context:collection} is .") + .Then + .ForCondition(subject => subject.All(x => x is not null)) + .FailWith("but found a null element.") + .Then + .ForCondition(subject => subject.All(x => expectedType == GetType(x))) + .FailWith("but found {0}.", subject => $"[{string.Join(", ", subject.Select(x => GetType(x).FullName))}]")); return new AndConstraint((TAssertions)this); } @@ -287,17 +286,16 @@ public AndConstraint AllBeOfType(Type expectedType, public AndConstraint BeEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { var singleItemArray = Subject?.Take(1).ToArray(); - Execute.Assertion + + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to be empty{reason}, ") - .Given(() => singleItemArray) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .ForCondition(subject => subject.Length == 0) - .FailWith("but found at least one item {0}.", singleItemArray) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to be empty{reason}, ", chain => chain + .Given(() => singleItemArray) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .ForCondition(subject => subject.Length == 0) + .FailWith("but found at least one item {0}.", singleItemArray)); return new AndConstraint((TAssertions)this); } @@ -355,7 +353,8 @@ public AndConstraint BeEquivalentTo(IEnumerable is . public AndConstraint BeEquivalentTo(IEnumerable expectation, Func, EquivalencyOptions> config, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(config); @@ -363,7 +362,8 @@ public AndConstraint BeEquivalentTo(IEnumerable()).AsCollection(); var context = - new EquivalencyValidationContext(Node.From>(() => AssertionScope.Current.CallerIdentity), + new EquivalencyValidationContext( + Node.From>(() => CallerIdentifier.DetermineCallerIdentity()), options) { Reason = new Reason(because, becauseArgs), @@ -425,8 +425,7 @@ public AndConstraint> BeInAscendingOrder /// is . public AndConstraint> BeInAscendingOrder( - IComparer comparer, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + IComparer comparer, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(comparer, nameof(comparer), "Cannot assert collection ordering without specifying a comparer."); @@ -457,7 +456,8 @@ public AndConstraint> BeInAscendingOrder( /// is . public AndConstraint> BeInAscendingOrder( Expression> propertyExpression, IComparer comparer, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(comparer, nameof(comparer), "Cannot assert collection ordering without specifying a comparer."); @@ -479,8 +479,7 @@ public AndConstraint> BeInAscendingOrder /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// - public AndConstraint> BeInAscendingOrder( - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint> BeInAscendingOrder(string because = "", params object[] becauseArgs) { return BeInAscendingOrder(GetComparer(), because, becauseArgs); } @@ -503,7 +502,8 @@ public AndConstraint> BeInAscendingOrder( /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// public AndConstraint> BeInAscendingOrder(Func comparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return BeInOrder(Comparer.Create((x, y) => comparison(x, y)), SortOrder.Ascending, because, becauseArgs); } @@ -526,8 +526,8 @@ public AndConstraint> BeInAscendingOrder(Func public AndConstraint> BeInDescendingOrder( - Expression> propertyExpression, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + Expression> propertyExpression, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return BeInDescendingOrder(propertyExpression, GetComparer(), because, becauseArgs); } @@ -551,8 +551,7 @@ public AndConstraint> BeInDescendingOrder /// is . public AndConstraint> BeInDescendingOrder( - IComparer comparer, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + IComparer comparer, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(comparer, nameof(comparer), "Cannot assert collection ordering without specifying a comparer."); @@ -583,7 +582,8 @@ public AndConstraint> BeInDescendingOrder( /// is . public AndConstraint> BeInDescendingOrder( Expression> propertyExpression, IComparer comparer, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(comparer, nameof(comparer), "Cannot assert collection ordering without specifying a comparer."); @@ -605,8 +605,7 @@ public AndConstraint> BeInDescendingOrder /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// - public AndConstraint> BeInDescendingOrder( - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint> BeInDescendingOrder(string because = "", params object[] becauseArgs) { return BeInDescendingOrder(GetComparer(), because, becauseArgs); } @@ -629,7 +628,8 @@ public AndConstraint> BeInDescendingOrder( /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// public AndConstraint> BeInDescendingOrder(Func comparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return BeInOrder(Comparer.Create((x, y) => comparison(x, y)), SortOrder.Descending, because, becauseArgs); } @@ -644,12 +644,12 @@ public AndConstraint> BeInDescendingOrder(Func /// Zero or more objects to format using the placeholders in . /// - public AndConstraint BeNullOrEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeNullOrEmpty(string because = "", params object[] becauseArgs) { var singleItemArray = Subject?.Take(1).ToArray(); var nullOrEmpty = singleItemArray is null || singleItemArray.Length == 0; - Execute.Assertion.ForCondition(nullOrEmpty) + assertionChain.ForCondition(nullOrEmpty) .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} to be null or empty{reason}, but found at least one item {0}.", @@ -671,23 +671,22 @@ public AndConstraint BeNullOrEmpty([StringSyntax("CompositeFormat") /// /// is . public AndConstraint BeSubsetOf(IEnumerable expectedSuperset, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expectedSuperset, nameof(expectedSuperset), "Cannot verify a subset against a collection."); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to be a subset of {0}{reason}, ", expectedSuperset) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .Given(subject => subject.Except(expectedSuperset)) - .ForCondition(excessItems => !excessItems.Any()) - .FailWith("but items {0} are not part of the superset.", excessItems => excessItems) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to be a subset of {0}{reason}, ", expectedSuperset, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .Given(subject => subject.Except(expectedSuperset)) + .ForCondition(excessItems => !excessItems.Any()) + .FailWith("but items {0} are not part of the superset.", excessItems => excessItems)); return new AndConstraint((TAssertions)this); } @@ -703,21 +702,21 @@ public AndConstraint BeSubsetOf(IEnumerable expectedSuperset, /// /// Zero or more objects to format using the placeholders in . /// - public AndWhichConstraint Contain(T expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndWhichConstraint Contain(T expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain {0}{reason}, but found .", expected); IEnumerable matches = []; - if (success) + if (assertionChain.Succeeded) { ICollection collection = Subject.ConvertOrCastToCollection(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(collection.Contains(expected)) .FailWith("Expected {context:collection} {0} to contain {1}{reason}.", collection, expected); @@ -746,25 +745,37 @@ public AndWhichConstraint Contain(Expression> pred { Guard.ThrowIfArgumentIsNull(predicate); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain {0}{reason}, but found .", predicate.Body); IEnumerable matches = []; - if (success) + int? firstMatchingIndex = null; + if (assertionChain.Succeeded) { Func func = predicate.Compile(); - Execute.Assertion - .ForCondition(Subject!.Any(func)) + foreach (var (item, index) in Subject!.Select((item, index) => (item, index))) + { + if (func(item)) + { + firstMatchingIndex = index; + break; + } + } + + assertionChain + .ForCondition(firstMatchingIndex.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to have an item matching {1}{reason}.", Subject, predicate.Body); matches = Subject.Where(func); } + assertionChain.WithCallerPostfix($"[{firstMatchingIndex}]").ReuseOnce(); + return new AndWhichConstraint((TAssertions)this, matches); } @@ -782,20 +793,20 @@ public AndWhichConstraint Contain(Expression> pred /// /// is . /// is empty. - public AndConstraint Contain(IEnumerable expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Contain(IEnumerable expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot verify containment against a collection"); ICollection expectedObjects = expected.ConvertOrCastToCollection(); Guard.ThrowIfArgumentIsEmpty(expectedObjects, nameof(expected), "Cannot verify containment against an empty collection"); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain {0}{reason}, but found .", expectedObjects); - if (success) + if (assertionChain.Succeeded) { IEnumerable missingItems = expectedObjects.Except(Subject!); @@ -803,14 +814,14 @@ public AndConstraint Contain(IEnumerable expected, { if (expectedObjects.Count > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to contain {1}{reason}, but could not find {2}.", Subject, expectedObjects, missingItems); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to contain {1}{reason}.", Subject, expectedObjects.Single()); @@ -844,7 +855,8 @@ public AndConstraint Contain(IEnumerable expected, /// Zero or more objects to format using the placeholders in . /// public AndWhichConstraint ContainEquivalentOf(TExpectation expectation, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return ContainEquivalentOf(expectation, config => config, because, becauseArgs); } @@ -879,27 +891,28 @@ public AndWhichConstraint ContainEquivalentOf(TExp /// /// is . public AndWhichConstraint ContainEquivalentOf(TExpectation expectation, - Func, EquivalencyOptions> config, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + Func, + EquivalencyOptions> config, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(config); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain equivalent of {0}{reason}, but found .", expectation); - if (success) + if (assertionChain.Succeeded) { EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); using var scope = new AssertionScope(); - scope.AddReportable("configuration", () => options.ToString()); + assertionChain.AddReportable("configuration", () => options.ToString()); - foreach (T actualItem in Subject!) + foreach ((T actualItem, int index) in Subject!.Select((item, index) => (item, index))) { var context = - new EquivalencyValidationContext(Node.From(() => AssertionScope.Current.CallerIdentity), + new EquivalencyValidationContext(Node.From(() => CurrentAssertionChain.CallerIdentifier), options) { Reason = new Reason(because, becauseArgs), @@ -919,11 +932,11 @@ public AndWhichConstraint ContainEquivalentOf(TExp if (failures.Length == 0) { - return new AndWhichConstraint((TAssertions)this, actualItem); + return new AndWhichConstraint((TAssertions)this, actualItem, assertionChain, $"[{index}]"); } } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to contain equivalent of {1}{reason}.", Subject, expectation); } @@ -957,16 +970,17 @@ public AndConstraint ContainInOrder(params T[] expected) /// /// is . public AndConstraint ContainInOrder(IEnumerable expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot verify ordered containment against a collection."); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain {0} in order{reason}, but found .", expected); - if (success) + if (assertionChain.Succeeded) { IList expectedItems = expected.ConvertOrCastToList(); IList actualItems = Subject.ConvertOrCastToList(); @@ -980,7 +994,7 @@ public AndConstraint ContainInOrder(IEnumerable expected, if (subjectIndex == -1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} {0} to contain items {1} in order{reason}" + @@ -1019,16 +1033,17 @@ public AndConstraint ContainInConsecutiveOrder(params T[] expected) /// /// is . public AndConstraint ContainInConsecutiveOrder(IEnumerable expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot verify ordered containment against a collection."); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain {0} in order{reason}, but found .", expected); - if (success) + if (assertionChain.Succeeded) { IList expectedItems = expected.ConvertOrCastToList(); @@ -1060,7 +1075,7 @@ public AndConstraint ContainInConsecutiveOrder(IEnumerable expec } } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} {0} to contain items {1} in order{reason}" + @@ -1081,21 +1096,18 @@ public AndConstraint ContainInConsecutiveOrder(IEnumerable expec /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint ContainItemsAssignableTo( - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint ContainItemsAssignableTo(string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .WithExpectation("Expected {context:collection} to contain at least one element assignable to type {0}{reason}, ", - typeof(TExpectation).FullName) - .ForCondition(Subject is not null) - .FailWith("but found .") - .Then - .Given(() => Subject.ConvertOrCastToCollection()) - .ForCondition(subject => subject.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", subject => subject.Select(x => GetType(x))) - .Then - .ClearExpectation(); + typeof(TExpectation).FullName, chain => chain + .ForCondition(Subject is not null) + .FailWith("but found .") + .Then + .Given(() => Subject.ConvertOrCastToCollection()) + .ForCondition(subject => subject.Any(x => typeof(TExpectation).IsAssignableFrom(GetType(x)))) + .FailWith("but found {0}.", subject => subject.Select(x => GetType(x)))); return new AndConstraint((TAssertions)this); } @@ -1110,8 +1122,8 @@ public AndConstraint ContainItemsAssignableTo( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotContainItemsAssignableTo( - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) => + public AndConstraint + NotContainItemsAssignableTo(string because = "", params object[] becauseArgs) => NotContainItemsAssignableTo(typeof(TExpectation), because, becauseArgs); /// @@ -1132,18 +1144,16 @@ public AndConstraint NotContainItemsAssignableTo(Type type, { Guard.ThrowIfArgumentIsNull(type); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .WithExpectation("Expected {context:collection} to not contain any elements assignable to type {0}{reason}, ", - type.FullName) - .ForCondition(Subject is not null) - .FailWith("but found .") - .Then - .Given(() => Subject.ConvertOrCastToCollection()) - .ForCondition(subject => subject.All(x => !type.IsAssignableFrom(GetType(x)))) - .FailWith("but found {0}.", subject => subject.Select(x => GetType(x))) - .Then - .ClearExpectation(); + type.FullName, chain => chain + .ForCondition(Subject is not null) + .FailWith("but found .") + .Then + .Given(() => Subject.ConvertOrCastToCollection()) + .ForCondition(subject => subject.All(x => !type.IsAssignableFrom(GetType(x)))) + .FailWith("but found {0}.", subject => subject.Select(x => GetType(x)))); return new AndConstraint((TAssertions)this); } @@ -1158,23 +1168,23 @@ public AndConstraint NotContainItemsAssignableTo(Type type, /// /// Zero or more objects to format using the placeholders in . /// - public AndWhichConstraint ContainSingle([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndWhichConstraint ContainSingle(string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain a single item{reason}, but found ."); T match = default; - if (success) + if (assertionChain.Succeeded) { ICollection actualItems = Subject.ConvertOrCastToCollection(); switch (actualItems.Count) { case 0: // Fail, Collection is empty - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} to contain a single item{reason}, but the collection is empty."); @@ -1183,7 +1193,7 @@ public AndWhichConstraint ContainSingle([StringSyntax("Composite match = actualItems.Single(); break; default: // Fail, Collection contains more than a single item - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} to contain a single item{reason}, but found {0}.", Subject); @@ -1191,7 +1201,7 @@ public AndWhichConstraint ContainSingle([StringSyntax("Composite } } - return new AndWhichConstraint((TAssertions)this, match); + return new AndWhichConstraint((TAssertions)this, match, assertionChain, "[0]"); } /// @@ -1207,25 +1217,25 @@ public AndWhichConstraint ContainSingle([StringSyntax("Composite /// /// is . public AndWhichConstraint ContainSingle(Expression> predicate, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(predicate); const string expectationPrefix = "Expected {context:collection} to contain a single item matching {0}{reason}, "; - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith(expectationPrefix + "but found .", predicate); T[] matches = []; - if (success) + if (assertionChain.Succeeded) { ICollection actualItems = Subject.ConvertOrCastToCollection(); - Execute.Assertion + assertionChain .ForCondition(actualItems.Count > 0) .BecauseOf(because, becauseArgs) .FailWith(expectationPrefix + "but the collection is empty.", predicate); @@ -1235,13 +1245,13 @@ public AndWhichConstraint ContainSingle(Expression if (count == 0) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith(expectationPrefix + "but no such item was found.", predicate); } else if (count > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( expectationPrefix + "but " + count.ToString(CultureInfo.InvariantCulture) + " such items were found.", @@ -1253,7 +1263,7 @@ public AndWhichConstraint ContainSingle(Expression } } - return new AndWhichConstraint((TAssertions)this, matches); + return new AndWhichConstraint((TAssertions)this, matches, assertionChain, "[0]"); } /// @@ -1270,8 +1280,8 @@ public AndWhichConstraint ContainSingle(Expression /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint EndWith(IEnumerable expectation, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint EndWith(IEnumerable expectation, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return EndWith(expectation, (a, b) => EqualityComparer.Default.Equals(a, b), because, becauseArgs); } @@ -1296,7 +1306,8 @@ public AndConstraint EndWith(IEnumerable expectation, /// is . public AndConstraint EndWith( IEnumerable expectation, Func equalityComparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expectation, nameof(expectation), "Cannot compare collection with ."); @@ -1318,8 +1329,8 @@ public AndConstraint EndWith( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint EndWith(T element, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint EndWith(T element, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return EndWith([element], ObjectExtensions.GetComparer(), because, becauseArgs); } @@ -1355,7 +1366,8 @@ public AndConstraint Equal(params T[] elements) /// public AndConstraint Equal( IEnumerable expectation, Func equalityComparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { AssertSubjectEquality(expectation, equalityComparison, because, becauseArgs); @@ -1374,8 +1386,8 @@ public AndConstraint Equal( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Equal(IEnumerable expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Equal(IEnumerable expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { AssertSubjectEquality(expected, ObjectExtensions.GetComparer(), because, becauseArgs); @@ -1393,19 +1405,19 @@ public AndConstraint Equal(IEnumerable expected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveCount(int expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveCount(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain {0} item(s){reason}, but found .", expected); - if (success) + if (assertionChain.Succeeded) { int actualCount = Subject!.Count(); - Execute.Assertion + assertionChain .ForCondition(actualCount == expected) .BecauseOf(because, becauseArgs) .FailWith( @@ -1429,17 +1441,18 @@ public AndConstraint HaveCount(int expected, /// /// is . public AndConstraint HaveCount(Expression> countPredicate, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(countPredicate, nameof(countPredicate), "Cannot compare collection count against a predicate."); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain {0} items{reason}, but found .", countPredicate.Body); - if (success) + if (assertionChain.Succeeded) { Func compiledPredicate = countPredicate.Compile(); @@ -1447,7 +1460,7 @@ public AndConstraint HaveCount(Expression> countPre if (!compiledPredicate(actualCount)) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} to have a count {0}{reason}, but count is {1}: {2}.", countPredicate.Body, actualCount, Subject); @@ -1469,20 +1482,19 @@ public AndConstraint HaveCount(Expression> countPre /// Zero or more objects to format using the placeholders in . /// public AndConstraint HaveCountGreaterThanOrEqualTo(int expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain at least {0} item(s){reason}, ", expected) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .Given(subject => subject.Count()) - .ForCondition(actualCount => actualCount >= expected) - .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to contain at least {0} item(s){reason}, ", expected, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .Given(subject => subject.Count()) + .ForCondition(actualCount => actualCount >= expected) + .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject)); return new AndConstraint((TAssertions)this); } @@ -1498,21 +1510,19 @@ public AndConstraint HaveCountGreaterThanOrEqualTo(int expected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveCountGreaterThan(int expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveCountGreaterThan(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain more than {0} item(s){reason}, ", expected) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .Given(subject => subject.Count()) - .ForCondition(actualCount => actualCount > expected) - .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to contain more than {0} item(s){reason}, ", expected, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .Given(subject => subject.Count()) + .ForCondition(actualCount => actualCount > expected) + .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject)); return new AndConstraint((TAssertions)this); } @@ -1531,18 +1541,16 @@ public AndConstraint HaveCountGreaterThan(int expected, public AndConstraint HaveCountLessThanOrEqualTo(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain at most {0} item(s){reason}, ", expected) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .Given(subject => subject.Count()) - .ForCondition(actualCount => actualCount <= expected) - .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to contain at most {0} item(s){reason}, ", expected, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .Given(subject => subject.Count()) + .ForCondition(actualCount => actualCount <= expected) + .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject)); return new AndConstraint((TAssertions)this); } @@ -1558,21 +1566,19 @@ public AndConstraint HaveCountLessThanOrEqualTo(int expected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveCountLessThan(int expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveCountLessThan(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain fewer than {0} item(s){reason}, ", expected) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .Given(subject => subject.Count()) - .ForCondition(actualCount => actualCount < expected) - .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to contain fewer than {0} item(s){reason}, ", expected, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .Given(subject => subject.Count()) + .ForCondition(actualCount => actualCount < expected) + .FailWith("but found {0}: {1}.", actualCount => actualCount, _ => Subject)); return new AndConstraint((TAssertions)this); } @@ -1591,35 +1597,36 @@ public AndConstraint HaveCountLessThan(int expected, /// Zero or more objects to format using the placeholders in . /// public AndWhichConstraint HaveElementAt(int index, T element, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to have element at index {0}{reason}, but found .", index); T actual = default; - if (success) + if (assertionChain.Succeeded) { if (index < Subject!.Count()) { actual = Subject.ElementAt(index); - Execute.Assertion + assertionChain .ForCondition(ObjectExtensions.GetComparer()(actual, element)) .BecauseOf(because, becauseArgs) .FailWith("Expected {0} at index {1}{reason}, but found {2}.", element, index, actual); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {0} at index {1}{reason}, but found no element.", element, index); } } - return new AndWhichConstraint((TAssertions)this, actual); + return new AndWhichConstraint((TAssertions)this, actual, assertionChain, $"[{index}]"); } /// @@ -1635,26 +1642,26 @@ public AndWhichConstraint HaveElementAt(int index, T element, /// Zero or more objects to format using the placeholders in . /// public AndConstraint HaveElementPreceding(T successor, T expectation, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to have {0} precede {1}{reason}, ", expectation, successor) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but the collection is .") - .Then - .ForCondition(subject => subject.Any()) - .FailWith("but the collection is empty.") - .Then - .ForCondition(subject => HasPredecessor(successor, subject)) - .FailWith("but found nothing.") - .Then - .Given(subject => PredecessorOf(successor, subject)) - .ForCondition(predecessor => ObjectExtensions.GetComparer()(predecessor, expectation)) - .FailWith("but found {0}.", predecessor => predecessor) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to have {0} precede {1}{reason}, ", expectation, successor, chain => + chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but the collection is .") + .Then + .ForCondition(subject => subject.Any()) + .FailWith("but the collection is empty.") + .Then + .ForCondition(subject => HasPredecessor(successor, subject)) + .FailWith("but found nothing.") + .Then + .Given(subject => PredecessorOf(successor, subject)) + .ForCondition(predecessor => ObjectExtensions.GetComparer()(predecessor, expectation)) + .FailWith("but found {0}.", predecessor => predecessor)); return new AndConstraint((TAssertions)this); } @@ -1672,26 +1679,26 @@ public AndConstraint HaveElementPreceding(T successor, T expectatio /// Zero or more objects to format using the placeholders in . /// public AndConstraint HaveElementSucceeding(T predecessor, T expectation, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to have {0} succeed {1}{reason}, ", expectation, predecessor) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but the collection is .") - .Then - .ForCondition(subject => subject.Any()) - .FailWith("but the collection is empty.") - .Then - .ForCondition(subject => HasSuccessor(predecessor, subject)) - .FailWith("but found nothing.") - .Then - .Given(subject => SuccessorOf(predecessor, subject)) - .ForCondition(successor => ObjectExtensions.GetComparer()(successor, expectation)) - .FailWith("but found {0}.", successor => successor) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to have {0} succeed {1}{reason}, ", expectation, predecessor, + chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but the collection is .") + .Then + .ForCondition(subject => subject.Any()) + .FailWith("but the collection is empty.") + .Then + .ForCondition(subject => HasSuccessor(predecessor, subject)) + .FailWith("but found nothing.") + .Then + .Given(subject => SuccessorOf(predecessor, subject)) + .ForCondition(successor => ObjectExtensions.GetComparer()(successor, expectation)) + .FailWith("but found {0}.", successor => successor)); return new AndConstraint((TAssertions)this); } @@ -1709,22 +1716,21 @@ public AndConstraint HaveElementSucceeding(T predecessor, T expecta /// /// is . public AndConstraint HaveSameCount(IEnumerable otherCollection, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(otherCollection, nameof(otherCollection), "Cannot verify count against a collection."); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to have ") - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("the same count as {0}{reason}, but found .", otherCollection) - .Then - .Given(subject => (actual: subject.Count(), expected: otherCollection.Count())) - .ForCondition(count => count.actual == count.expected) - .FailWith("{0} item(s){reason}, but found {1}.", count => count.expected, count => count.actual) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to have ", chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("the same count as {0}{reason}, but found .", otherCollection) + .Then + .Given(subject => (actual: subject.Count(), expected: otherCollection.Count())) + .ForCondition(count => count.actual == count.expected) + .FailWith("{0} item(s){reason}, but found {1}.", count => count.expected, count => count.actual)); return new AndConstraint((TAssertions)this); } @@ -1742,21 +1748,22 @@ public AndConstraint HaveSameCount(IEnumerable /// is . public AndConstraint IntersectWith(IEnumerable otherCollection, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(otherCollection, nameof(otherCollection), "Cannot verify intersection against a collection."); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to intersect with {0}{reason}, but found .", otherCollection); - if (success) + if (assertionChain.Succeeded) { IEnumerable sharedItems = Subject!.Intersect(otherCollection); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(sharedItems.Any()) .FailWith( @@ -1777,19 +1784,17 @@ public AndConstraint IntersectWith(IEnumerable otherCollection, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeEmpty(string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} not to be empty{reason}") - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith(", but found .") - .Then - .ForCondition(subject => subject.Any()) - .FailWith(".") - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} not to be empty{reason}", chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith(", but found .") + .Then + .ForCondition(subject => subject.Any()) + .FailWith(".")); return new AndConstraint((TAssertions)this); } @@ -1808,20 +1813,21 @@ public AndConstraint NotBeEmpty([StringSyntax("CompositeFormat")] s /// /// is . public AndConstraint NotBeEquivalentTo(IEnumerable unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot verify inequivalence against a collection."); if (Subject is null) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} not to be equivalent{reason}, but found ."); } if (ReferenceEquals(Subject, unexpected)) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} {0} not to be equivalent with collection {1}{reason}, but they both reference the same object.", @@ -1852,13 +1858,13 @@ public AndConstraint NotBeEquivalentTo(IEnumerable public AndConstraint NotBeEquivalentTo(IEnumerable unexpected, Func, EquivalencyOptions> config, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot verify inequivalence against a collection."); if (Subject is null) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} not to be equivalent{reason}, but found ."); } @@ -1872,7 +1878,7 @@ public AndConstraint NotBeEquivalentTo(IEnumerable 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} not to be equivalent to collection {1}{reason}.", Subject, @@ -1899,8 +1905,8 @@ public AndConstraint NotBeEquivalentTo(IEnumerable public AndConstraint NotBeInAscendingOrder( - Expression> propertyExpression, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + Expression> propertyExpression, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotBeInAscendingOrder(propertyExpression, GetComparer(), because, becauseArgs); } @@ -1924,8 +1930,7 @@ public AndConstraint NotBeInAscendingOrder( /// /// is . public AndConstraint NotBeInAscendingOrder( - IComparer comparer, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + IComparer comparer, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(comparer, nameof(comparer), "Cannot assert collection ordering without specifying a comparer."); @@ -1956,7 +1961,8 @@ public AndConstraint NotBeInAscendingOrder( /// is . public AndConstraint NotBeInAscendingOrder( Expression> propertyExpression, IComparer comparer, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(comparer, nameof(comparer), "Cannot assert collection ordering without specifying a comparer."); @@ -1978,7 +1984,7 @@ public AndConstraint NotBeInAscendingOrder( /// /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// - public AndConstraint NotBeInAscendingOrder([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeInAscendingOrder(string because = "", params object[] becauseArgs) { return NotBeInAscendingOrder(GetComparer(), because, becauseArgs); } @@ -2000,7 +2006,8 @@ public AndConstraint NotBeInAscendingOrder([StringSyntax("Composite /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// public AndConstraint NotBeInAscendingOrder(Func comparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotBeInOrder(Comparer.Create((x, y) => comparison(x, y)), SortOrder.Ascending, because, becauseArgs); } @@ -2023,8 +2030,8 @@ public AndConstraint NotBeInAscendingOrder(Func comparis /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// public AndConstraint NotBeInDescendingOrder( - Expression> propertyExpression, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + Expression> propertyExpression, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotBeInDescendingOrder(propertyExpression, GetComparer(), because, becauseArgs); } @@ -2079,7 +2086,8 @@ public AndConstraint NotBeInDescendingOrder( /// is . public AndConstraint NotBeInDescendingOrder( Expression> propertyExpression, IComparer comparer, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(comparer, nameof(comparer), "Cannot assert collection ordering without specifying a comparer."); @@ -2101,7 +2109,7 @@ public AndConstraint NotBeInDescendingOrder( /// /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// - public AndConstraint NotBeInDescendingOrder([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeInDescendingOrder(string because = "", params object[] becauseArgs) { return NotBeInDescendingOrder(GetComparer(), because, becauseArgs); } @@ -2123,7 +2131,8 @@ public AndConstraint NotBeInDescendingOrder([StringSyntax("Composit /// Empty and single element collections are considered to be ordered both in ascending and descending order at the same time. /// public AndConstraint NotBeInDescendingOrder(Func comparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotBeInOrder(Comparer.Create((x, y) => comparison(x, y)), SortOrder.Descending, because, becauseArgs); } @@ -2138,7 +2147,7 @@ public AndConstraint NotBeInDescendingOrder(Func compari /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeNullOrEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeNullOrEmpty(string because = "", params object[] becauseArgs) { return NotBeNull(because, becauseArgs) .And.NotBeEmpty(because, becauseArgs); @@ -2156,18 +2165,19 @@ public AndConstraint NotBeNullOrEmpty([StringSyntax("CompositeForma /// Zero or more objects to format using the placeholders in . /// public AndConstraint NotBeSubsetOf(IEnumerable unexpectedSuperset, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Cannot assert a collection against a subset."); - if (success) + if (assertionChain.Succeeded) { if (ReferenceEquals(Subject, unexpectedSuperset)) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Did not expect {context:collection} {0} to be a subset of {1}{reason}, but they both reference the same object.", @@ -2179,7 +2189,7 @@ public AndConstraint NotBeSubsetOf(IEnumerable unexpectedSuperse if (actualItems.Intersect(unexpectedSuperset).Count() == actualItems.Count) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:collection} {0} to be a subset of {1}{reason}.", actualItems, unexpectedSuperset); @@ -2200,23 +2210,23 @@ public AndConstraint NotBeSubsetOf(IEnumerable unexpectedSuperse /// /// Zero or more objects to format using the placeholders in . /// - public AndWhichConstraint NotContain(T unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotContain(T unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to not contain {0}{reason}, but found .", unexpected); IEnumerable matched = []; - if (success) + if (assertionChain.Succeeded) { ICollection collection = Subject.ConvertOrCastToCollection(); if (collection.Contains(unexpected)) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to not contain {1}{reason}.", collection, unexpected); } @@ -2224,7 +2234,7 @@ public AndWhichConstraint NotContain(T unexpected, matched = collection.Where(item => !EqualityComparer.Default.Equals(item, unexpected)); } - return new AndWhichConstraint((TAssertions)this, matched); + return new AndConstraint((TAssertions)this); } /// @@ -2240,21 +2250,22 @@ public AndWhichConstraint NotContain(T unexpected, /// /// is . public AndConstraint NotContain(Expression> predicate, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(predicate); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} not to contain {0}{reason}, but found .", predicate.Body); - if (success) + if (assertionChain.Succeeded) { Func compiledPredicate = predicate.Compile(); IEnumerable unexpectedItems = Subject!.Where(item => compiledPredicate(item)); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!unexpectedItems.Any()) .FailWith("Expected {context:collection} {0} to not have any items matching {1}{reason}, but found {2}.", @@ -2278,8 +2289,8 @@ public AndConstraint NotContain(Expression> predicate /// /// is . /// is empty. - public AndConstraint NotContain(IEnumerable unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotContain(IEnumerable unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot verify non-containment against a collection"); @@ -2288,12 +2299,12 @@ public AndConstraint NotContain(IEnumerable unexpected, Guard.ThrowIfArgumentIsEmpty(unexpectedObjects, nameof(unexpected), "Cannot verify non-containment against an empty collection"); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to not contain {0}{reason}, but found .", unexpected); - if (success) + if (assertionChain.Succeeded) { IEnumerable foundItems = unexpectedObjects.Intersect(Subject!); @@ -2301,14 +2312,14 @@ public AndConstraint NotContain(IEnumerable unexpected, { if (unexpectedObjects.Count > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to not contain {1}{reason}, but found {2}.", Subject, unexpected, foundItems); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to not contain {1}{reason}.", Subject, unexpectedObjects.First()); @@ -2342,7 +2353,8 @@ public AndConstraint NotContain(IEnumerable unexpected, /// Zero or more objects to format using the placeholders in . /// public AndConstraint NotContainEquivalentOf(TExpectation unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotContainEquivalentOf(unexpected, config => config, because, becauseArgs); } @@ -2378,18 +2390,19 @@ public AndConstraint NotContainEquivalentOf(TExpectat /// is . [SuppressMessage("Design", "MA0051:Method is too long", Justification = "Needs refactoring")] public AndConstraint NotContainEquivalentOf(TExpectation unexpected, - Func, EquivalencyOptions> config, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + Func, + EquivalencyOptions> config, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(config); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} not to contain equivalent of {0}{reason}, but collection is .", unexpected); - if (success) + if (assertionChain.Succeeded) { EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); @@ -2402,7 +2415,7 @@ public AndConstraint NotContainEquivalentOf(TExpectat foreach (T actualItem in Subject!) { var context = - new EquivalencyValidationContext(Node.From(() => AssertionScope.Current.CallerIdentity), + new EquivalencyValidationContext(Node.From(() => CurrentAssertionChain.CallerIdentifier), options) { Reason = new Reason(because, becauseArgs), @@ -2433,22 +2446,21 @@ public AndConstraint NotContainEquivalentOf(TExpectat { using (new AssertionScope()) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) + .WithReportable("configuration", () => options.ToString()) .WithExpectation("Expected {context:collection} {0} not to contain equivalent of {1}{reason}, ", Subject, - unexpected) - .AddReportable("configuration", () => options.ToString()); - - if (foundIndices.Count == 1) - { - Execute.Assertion - .FailWith("but found one at index {0}.", foundIndices[0]); - } - else - { - Execute.Assertion - .FailWith("but found several at indices {0}.", foundIndices); - } + unexpected, chain => + { + if (foundIndices.Count == 1) + { + chain.FailWith("but found one at index {0}.", foundIndices[0]); + } + else + { + chain.FailWith("but found several at indices {0}.", foundIndices); + } + }); } } } @@ -2485,14 +2497,15 @@ public AndConstraint NotContainInOrder(params T[] unexpected) /// /// is . public AndConstraint NotContainInOrder(IEnumerable unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot verify absence of ordered containment against a collection."); if (Subject is null) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Cannot verify absence of ordered containment in a collection."); @@ -2516,7 +2529,7 @@ public AndConstraint NotContainInOrder(IEnumerable unexpected, } } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} {0} to not contain items {1} in order{reason}, " + @@ -2556,14 +2569,15 @@ public AndConstraint NotContainInConsecutiveOrder(params T[] unexpe /// /// is . public AndConstraint NotContainInConsecutiveOrder(IEnumerable unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot verify absence of ordered containment against a collection."); if (Subject is null) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Cannot verify absence of ordered containment in a collection."); @@ -2593,7 +2607,7 @@ public AndConstraint NotContainInConsecutiveOrder(IEnumerable un if (consecutiveItems == unexpectedItems.Count) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} {0} to not contain items {1} in consecutive order{reason}, " + @@ -2622,17 +2636,18 @@ public AndConstraint NotContainInConsecutiveOrder(IEnumerable un /// /// is . public AndConstraint NotContainNulls(Expression> predicate, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) where TKey : class { Guard.ThrowIfArgumentIsNull(predicate); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} not to contain s{reason}, but collection is ."); - if (success) + if (assertionChain.Succeeded) { Func compiledPredicate = predicate.Compile(); @@ -2640,7 +2655,7 @@ public AndConstraint NotContainNulls(Expression .Where(e => compiledPredicate(e) is null) .ToArray(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(values.Length == 0) .FailWith("Expected {context:collection} not to contain s on {0}{reason}, but found {1}.", @@ -2660,14 +2675,14 @@ public AndConstraint NotContainNulls(Expression /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotContainNulls([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotContainNulls(string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} not to contain s{reason}, but collection is ."); - if (success) + if (assertionChain.Succeeded) { int[] indices = Subject! .Select((item, index) => (Item: item, Index: index)) @@ -2679,7 +2694,7 @@ public AndConstraint NotContainNulls([StringSyntax("CompositeFormat { if (indices.Length > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} not to contain s{reason}, but found several at indices {0}.", @@ -2687,7 +2702,7 @@ public AndConstraint NotContainNulls([StringSyntax("CompositeFormat } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} not to contain s{reason}, but found one at index {0}.", indices[0]); @@ -2711,26 +2726,25 @@ public AndConstraint NotContainNulls([StringSyntax("CompositeFormat /// Zero or more objects to format using the placeholders in . /// /// is . - public AndConstraint NotEqual(IEnumerable unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotEqual(IEnumerable unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot compare collection with ."); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected collections not to be equal{reason}, ") - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .ForCondition(subject => !ReferenceEquals(subject, unexpected)) - .FailWith("but they both reference the same object.") - .Then - .ClearExpectation() + .WithExpectation("Expected collections not to be equal{reason}, ", chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .ForCondition(subject => !ReferenceEquals(subject, unexpected)) + .FailWith("but they both reference the same object.")) .Then - .Given(subject => subject.ConvertOrCastToCollection()) + .Given(() => Subject.ConvertOrCastToCollection()) .ForCondition(actualItems => !actualItems.SequenceEqual(unexpected)) - .FailWith("Did not expect collections {0} and {1} to be equal{reason}.", _ => unexpected, actualItems => actualItems); + .FailWith("Did not expect collections {0} and {1} to be equal{reason}.", _ => unexpected, + actualItems => actualItems); return new AndConstraint((TAssertions)this); } @@ -2746,21 +2760,19 @@ public AndConstraint NotEqual(IEnumerable unexpected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveCount(int unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveCount(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to not contain {0} item(s){reason}, ", unexpected) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but found .") - .Then - .Given(subject => subject.Count()) - .ForCondition(actualCount => actualCount != unexpected) - .FailWith("but found {0}.", actualCount => actualCount) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to not contain {0} item(s){reason}, ", unexpected, chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but found .") + .Then + .Given(subject => subject.Count()) + .ForCondition(actualCount => actualCount != unexpected) + .FailWith("but found {0}.", actualCount => actualCount)); return new AndConstraint((TAssertions)this); } @@ -2778,11 +2790,12 @@ public AndConstraint NotHaveCount(int unexpected, /// /// is . public AndConstraint NotHaveSameCount(IEnumerable otherCollection, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(otherCollection, nameof(otherCollection), "Cannot verify count against a collection."); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .Given(() => Subject) .ForCondition(subject => subject is not null) @@ -2817,12 +2830,13 @@ public AndConstraint NotHaveSameCount(IEnumerable /// is . public AndConstraint NotIntersectWith(IEnumerable otherCollection, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(otherCollection, nameof(otherCollection), "Cannot verify intersection against a collection."); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .Given(() => Subject) .ForCondition(subject => subject is not null) @@ -2857,25 +2871,23 @@ public AndConstraint NotIntersectWith(IEnumerable otherCollectio /// /// is . public AndConstraint OnlyContain( - Expression> predicate, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + Expression> predicate, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(predicate); Func compiledPredicate = predicate.Compile(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain only items matching {0}{reason}, ", predicate.Body) - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but the collection is .") - .Then - .Given(subject => subject.ConvertOrCastToCollection().Where(item => !compiledPredicate(item))) - .ForCondition(mismatchingItems => !mismatchingItems.Any()) - .FailWith("but {0} do(es) not match.", mismatchingItems => mismatchingItems) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to contain only items matching {0}{reason}, ", predicate.Body, + chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but the collection is .") + .Then + .Given(subject => subject.ConvertOrCastToCollection().Where(item => !compiledPredicate(item))) + .ForCondition(mismatchingItems => !mismatchingItems.Any()) + .FailWith("but {0} do(es) not match.", mismatchingItems => mismatchingItems)); return new AndConstraint((TAssertions)this); } @@ -2893,16 +2905,17 @@ public AndConstraint OnlyContain( /// /// is . public AndConstraint OnlyHaveUniqueItems(Expression> predicate, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(predicate); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to only have unique items{reason}, but found ."); - if (success) + if (assertionChain.Succeeded) { Func compiledPredicate = predicate.Compile(); @@ -2915,7 +2928,7 @@ public AndConstraint OnlyHaveUniqueItems(Expression 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} to only have unique items on {0}{reason}, but items {1} are not unique.", @@ -2924,7 +2937,7 @@ public AndConstraint OnlyHaveUniqueItems(Expression OnlyHaveUniqueItems(Expression /// Zero or more objects to format using the placeholders in . /// - public AndConstraint OnlyHaveUniqueItems([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint OnlyHaveUniqueItems(string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to only have unique items{reason}, but found ."); - if (success) + if (assertionChain.Succeeded) { - IEnumerable groupWithMultipleItems = Subject! + T[] groupWithMultipleItems = Subject! .GroupBy(o => o) .Where(g => g.Count() > 1) - .Select(g => g.Key); + .Select(g => g.Key) + .ToArray(); - if (groupWithMultipleItems.Any()) + if (groupWithMultipleItems.Length > 0) { - if (groupWithMultipleItems.Count() > 1) + if (groupWithMultipleItems.Length > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:collection} to only have unique items{reason}, but items {0} are not unique.", @@ -2973,10 +2987,10 @@ public AndConstraint OnlyHaveUniqueItems([StringSyntax("CompositeFo } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} to only have unique items{reason}, but item {0} is not unique.", - groupWithMultipleItems.First()); + groupWithMultipleItems[0]); } } } @@ -2999,21 +3013,20 @@ public AndConstraint OnlyHaveUniqueItems([StringSyntax("CompositeFo /// Zero or more objects to format using the placeholders in . /// /// is . - public AndConstraint AllSatisfy(Action expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint AllSatisfy(Action expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot verify against a inspector"); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain only items satisfying the inspector{reason}, ") - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but collection is .") - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to contain only items satisfying the inspector{reason}, ", + chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but collection is .")); - if (success) + if (assertionChain.Succeeded) { string[] failuresFromInspectors; @@ -3028,12 +3041,11 @@ public AndConstraint AllSatisfy(Action expected, string failureMessage = Environment.NewLine + string.Join(Environment.NewLine, failuresFromInspectors.Select(x => x.IndentLines())); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to contain only items satisfying the inspector{reason}:") - .FailWithPreFormatted(failureMessage) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to contain only items satisfying the inspector{reason}:", + chain => chain + .FailWithPreFormatted(failureMessage)); } return new AndConstraint((TAssertions)this); @@ -3075,7 +3087,8 @@ public AndConstraint SatisfyRespectively(params Action[] element /// is . /// is empty. public AndConstraint SatisfyRespectively(IEnumerable> expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot verify against a collection of inspectors"); @@ -3084,25 +3097,23 @@ public AndConstraint SatisfyRespectively(IEnumerable> exp Guard.ThrowIfArgumentIsEmpty(elementInspectors, nameof(expected), "Cannot verify against an empty collection of inspectors"); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to satisfy all inspectors{reason}, ") - .Given(() => Subject) - .ForCondition(subject => subject is not null) - .FailWith("but collection is .") - .Then - .ForCondition(subject => subject.Any()) - .FailWith("but collection is empty.") - .Then - .ClearExpectation() + .WithExpectation("Expected {context:collection} to satisfy all inspectors{reason}, ", chain => chain + .Given(() => Subject) + .ForCondition(subject => subject is not null) + .FailWith("but collection is .") + .Then + .ForCondition(subject => subject.Any()) + .FailWith("but collection is empty.")) .Then - .Given(subject => (elements: subject.Count(), inspectors: elementInspectors.Count)) + .Given(() => (elements: Subject.Count(), inspectors: elementInspectors.Count)) .ForCondition(count => count.elements == count.inspectors) .FailWith( "Expected {context:collection} to contain exactly {0} items{reason}, but it contains {1} items", count => count.inspectors, count => count.elements); - if (success) + if (assertionChain.Succeeded) { string[] failuresFromInspectors; @@ -3116,13 +3127,12 @@ public AndConstraint SatisfyRespectively(IEnumerable> exp string failureMessage = Environment.NewLine + string.Join(Environment.NewLine, failuresFromInspectors.Select(x => x.IndentLines())); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .WithExpectation( - "Expected {context:collection} to satisfy all inspectors{reason}, but some inspectors are not satisfied:") - .FailWithPreFormatted(failureMessage) - .Then - .ClearExpectation(); + "Expected {context:collection} to satisfy all inspectors{reason}, but some inspectors are not satisfied:", + chain => chain + .FailWithPreFormatted(failureMessage)); } } @@ -3165,7 +3175,8 @@ public AndConstraint Satisfy(params Expression>[] pre /// is . /// is empty. public AndConstraint Satisfy(IEnumerable>> predicates, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(predicates, nameof(predicates), "Cannot verify against a collection of predicates"); @@ -3174,7 +3185,7 @@ public AndConstraint Satisfy(IEnumerable>> Guard.ThrowIfArgumentIsEmpty(predicatesList, nameof(predicates), "Cannot verify against an empty collection of predicates"); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .Given(() => Subject) .ForCondition(subject => subject is not null) @@ -3183,7 +3194,7 @@ public AndConstraint Satisfy(IEnumerable>> .ForCondition(subject => subject.Any()) .FailWith("Expected {context:collection} to satisfy all predicates{reason}, but collection is empty."); - if (success) + if (assertionChain.Succeeded) { MaximumMatchingSolution maximumMatchingSolution = new MaximumMatchingProblem(predicatesList, Subject).Solve(); @@ -3215,10 +3226,10 @@ public AndConstraint Satisfy(IEnumerable>> message += doubleNewLine + string.Join(doubleNewLine, elementDescriptions); } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to satisfy all predicates{reason}, but:") - .FailWithPreFormatted(message); + .WithExpectation("Expected {context:collection} to satisfy all predicates{reason}, but:", chain => chain + .FailWithPreFormatted(message)); } } @@ -3240,8 +3251,8 @@ public AndConstraint Satisfy(IEnumerable>> /// Zero or more objects to format using the placeholders in . /// /// is . - public AndConstraint StartWith(IEnumerable expectation, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint StartWith(IEnumerable expectation, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return StartWith(expectation, (a, b) => EqualityComparer.Default.Equals(a, b), because, becauseArgs); } @@ -3266,7 +3277,8 @@ public AndConstraint StartWith(IEnumerable expectation, /// is . public AndConstraint StartWith( IEnumerable expectation, Func equalityComparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expectation, nameof(expectation), "Cannot compare collection with ."); @@ -3288,8 +3300,8 @@ public AndConstraint StartWith( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint StartWith(T element, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint StartWith(T element, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return StartWith([element], ObjectExtensions.GetComparer(), because, becauseArgs); } @@ -3298,7 +3310,7 @@ internal AndConstraint> BeOrderedBy( Expression> propertyExpression, IComparer comparer, SortOrder direction, - [StringSyntax("CompositeFormat")] string because, + string because, object[] becauseArgs) { if (IsValidProperty(propertyExpression, because, becauseArgs)) @@ -3311,18 +3323,18 @@ internal AndConstraint> BeOrderedBy( direction, unordered); - Execute.Assertion + assertionChain .ForCondition(unordered.SequenceEqual(expectation)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to be ordered {1}{reason} and result in {2}.", () => Subject, () => GetExpressionOrderString(propertyExpression), () => expectation); return new AndConstraint>( - new SubsequentOrderingAssertions(Subject, expectation)); + new SubsequentOrderingAssertions(Subject, expectation, assertionChain)); } return new AndConstraint>( - new SubsequentOrderingAssertions(Subject, Enumerable.Empty().OrderBy(x => x))); + new SubsequentOrderingAssertions(Subject, Enumerable.Empty().OrderBy(x => x), assertionChain)); } internal virtual IOrderedEnumerable GetOrderedEnumerable( @@ -3352,50 +3364,49 @@ protected static IEnumerable RepeatAsManyAs(TExpecta protected void AssertCollectionEndsWith(IEnumerable actual, ICollection expected, Func equalityComparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(equalityComparison); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to end with {0}{reason}, ", expected) - .Given(() => actual) - .AssertCollectionIsNotNull() - .Then - .AssertCollectionHasEnoughItems(expected.Count) - .Then - .AssertCollectionsHaveSameItems(expected, (a, e) => - { - int firstIndexToCompare = a.Count - e.Count; - int index = a.Skip(firstIndexToCompare).IndexOfFirstDifferenceWith(e, equalityComparison); - return index >= 0 ? index + firstIndexToCompare : index; - }) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to end with {0}{reason}, ", expected, chain => chain + .Given(() => actual) + .AssertCollectionIsNotNull() + .Then + .AssertCollectionHasEnoughItems(expected.Count) + .Then + .AssertCollectionsHaveSameItems(expected, (a, e) => + { + int firstIndexToCompare = a.Count - e.Count; + int index = a.Skip(firstIndexToCompare).IndexOfFirstDifferenceWith(e, equalityComparison); + return index >= 0 ? index + firstIndexToCompare : index; + })); } protected void AssertCollectionStartsWith(IEnumerable actualItems, ICollection expected, Func equalityComparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(equalityComparison); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:collection} to start with {0}{reason}, ", expected) - .Given(() => actualItems) - .AssertCollectionIsNotNull() - .Then - .AssertCollectionHasEnoughItems(expected.Count) - .Then - .AssertCollectionsHaveSameItems(expected, (a, e) => a.Take(e.Count).IndexOfFirstDifferenceWith(e, equalityComparison)) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to start with {0}{reason}, ", expected, chain => chain + .Given(() => actualItems) + .AssertCollectionIsNotNull() + .Then + .AssertCollectionHasEnoughItems(expected.Count) + .Then + .AssertCollectionsHaveSameItems(expected, + (a, e) => a.Take(e.Count).IndexOfFirstDifferenceWith(e, equalityComparison))); } protected void AssertSubjectEquality(IEnumerable expectation, - Func equalityComparison, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + Func equalityComparison, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(equalityComparison); @@ -3411,21 +3422,16 @@ protected void AssertSubjectEquality(IEnumerable exp ICollection expectedItems = expectation.ConvertOrCastToCollection(); - AssertionScope assertion = Execute.Assertion.BecauseOf(because, becauseArgs); - - if (subjectIsNull) - { - assertion.FailWith("Expected {context:collection} to be equal to {0}{reason}, but found .", expectedItems); - } - - assertion - .WithExpectation("Expected {context:collection} to be equal to {0}{reason}, ", expectedItems) - .Given(() => Subject.ConvertOrCastToCollection()) - .AssertCollectionsHaveSameCount(expectedItems.Count) - .Then - .AssertCollectionsHaveSameItems(expectedItems, (a, e) => a.IndexOfFirstDifferenceWith(e, equalityComparison)) + assertionChain + .BecauseOf(because, becauseArgs) + .ForCondition(!subjectIsNull) + .FailWith("Expected {context:collection} to be equal to {0}{reason}, but found .", expectedItems) .Then - .ClearExpectation(); + .WithExpectation("Expected {context:collection} to be equal to {0}{reason}, ", expectedItems, chain => chain + .Given(() => Subject.ConvertOrCastToCollection()) + .AssertCollectionsHaveSameCount(expectedItems.Count) + .Then + .AssertCollectionsHaveSameItems(expectedItems, (a, e) => a.IndexOfFirstDifferenceWith(e, equalityComparison))); } private static string GetExpressionOrderString(Expression> propertyExpression) @@ -3512,25 +3518,28 @@ private string[] CollectFailuresFromInspectors(IEnumerable> elementIns } private bool IsValidProperty(Expression> propertyExpression, - [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because, + object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(propertyExpression, nameof(propertyExpression), "Cannot assert collection ordering without specifying a property."); propertyExpression.ValidateMemberPath(); - return Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to be ordered by {0}{reason} but found .", () => propertyExpression.GetMemberPath()); + + return assertionChain.Succeeded; } private AndConstraint NotBeOrderedBy( Expression> propertyExpression, IComparer comparer, SortOrder direction, - [StringSyntax("CompositeFormat")] string because, + string because, object[] becauseArgs) { if (IsValidProperty(propertyExpression, because, becauseArgs)) @@ -3543,7 +3552,7 @@ private AndConstraint NotBeOrderedBy( direction, unordered); - Execute.Assertion + assertionChain .ForCondition(!unordered.SequenceEqual(expectation)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} {0} to not be ordered {1}{reason} and not result in {2}.", @@ -3558,19 +3567,19 @@ private AndConstraint NotBeOrderedBy( /// Elements are compared using their implementation. /// private AndConstraint> BeInOrder( - IComparer comparer, SortOrder expectedOrder, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + IComparer comparer, SortOrder expectedOrder, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { string sortOrder = expectedOrder == SortOrder.Ascending ? "ascending" : "descending"; - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith($"Expected {{context:collection}} to be in {sortOrder} order{{reason}}, but found ."); IOrderedEnumerable ordering = Array.Empty().OrderBy(x => x); - if (success) + if (assertionChain.Succeeded) { IList actualItems = Subject.ConvertOrCastToList(); @@ -3585,19 +3594,20 @@ private AndConstraint> BeInOrder( { if (!areSameOrEqual(actualItems[index], orderedItems[index])) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:collection} to be in " + sortOrder + " order{reason}, but found {0} where item at index {1} is in wrong order.", actualItems, index); return new AndConstraint>( - new SubsequentOrderingAssertions(Subject, Enumerable.Empty().OrderBy(x => x))); + new SubsequentOrderingAssertions(Subject, Enumerable.Empty().OrderBy(x => x), assertionChain)); } } } - return new AndConstraint>(new SubsequentOrderingAssertions(Subject, ordering)); + return new AndConstraint>( + new SubsequentOrderingAssertions(Subject, ordering, assertionChain)); } /// @@ -3605,16 +3615,17 @@ private AndConstraint> BeInOrder( /// using their implementation. /// private AndConstraint NotBeInOrder(IComparer comparer, SortOrder order, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { string sortOrder = order == SortOrder.Ascending ? "ascending" : "descending"; - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith($"Did not expect {{context:collection}} to be in {sortOrder} order{{reason}}, but found ."); - if (success) + if (assertionChain.Succeeded) { IList actualItems = Subject.ConvertOrCastToList(); @@ -3628,7 +3639,7 @@ private AndConstraint NotBeInOrder(IComparer comparer, SortOrder .Where((actualItem, index) => !areSameOrEqual(actualItem, orderedItems[index])) .Any(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(itemsAreUnordered) .FailWith( diff --git a/Src/FluentAssertions/Collections/GenericDictionaryAssertions.cs b/Src/FluentAssertions/Collections/GenericDictionaryAssertions.cs index 765a90f2ad..381e082a1b 100644 --- a/Src/FluentAssertions/Collections/GenericDictionaryAssertions.cs +++ b/Src/FluentAssertions/Collections/GenericDictionaryAssertions.cs @@ -17,8 +17,8 @@ public class GenericDictionaryAssertions : GenericDictionaryAssertions> where TCollection : IEnumerable> { - public GenericDictionaryAssertions(TCollection keyValuePairs) - : base(keyValuePairs) + public GenericDictionaryAssertions(TCollection keyValuePairs, AssertionChain assertionChain) + : base(keyValuePairs, assertionChain) { } } @@ -26,15 +26,17 @@ public GenericDictionaryAssertions(TCollection keyValuePairs) /// /// Contains a number of methods to assert that a is in the expected state. /// -[DebuggerNonUserCode] public class GenericDictionaryAssertions : GenericCollectionAssertions, TAssertions> where TCollection : IEnumerable> where TAssertions : GenericDictionaryAssertions { - public GenericDictionaryAssertions(TCollection keyValuePairs) - : base(keyValuePairs) + private readonly AssertionChain assertionChain; + + public GenericDictionaryAssertions(TCollection keyValuePairs, AssertionChain assertionChain) + : base(keyValuePairs, assertionChain) { + this.assertionChain = assertionChain; } #region Equal @@ -59,12 +61,12 @@ public AndConstraint Equal(T expected, { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot compare dictionary with ."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to be equal to {0}{reason}, but found {1}.", expected, Subject); - if (success) + if (assertionChain.Succeeded) { IEnumerable subjectKeys = GetKeys(Subject); IEnumerable expectedKeys = GetKeys(expected); @@ -73,7 +75,7 @@ public AndConstraint Equal(T expected, if (missingKeys.Any()) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to be equal to {0}{reason}, but could not find keys {1}.", expected, missingKeys); @@ -81,9 +83,10 @@ public AndConstraint Equal(T expected, if (additionalKeys.Any()) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:dictionary} to be equal to {0}{reason}, but found additional keys {1}.", expected, + .FailWith("Expected {context:dictionary} to be equal to {0}{reason}, but found additional keys {1}.", + expected, additionalKeys); } @@ -91,7 +94,7 @@ public AndConstraint Equal(T expected, foreach (var key in expectedKeys) { - Execute.Assertion + assertionChain .ForCondition(areSameOrEqual(GetValue(Subject, key), GetValue(expected, key))) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to be equal to {0}{reason}, but {1} differs at key {2}.", @@ -122,16 +125,16 @@ public AndConstraint NotEqual(T unexpected, { Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot compare dictionary with ."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected dictionaries not to be equal{reason}, but found {0}.", Subject); - if (success) + if (assertionChain.Succeeded) { if (ReferenceEquals(Subject, unexpected)) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected dictionaries not to be equal{reason}, but they both reference the same object."); } @@ -149,7 +152,7 @@ public AndConstraint NotEqual(T unexpected, if (!foundDifference) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Did not expect dictionaries {0} and {1} to be equal{reason}.", unexpected, Subject); } @@ -220,7 +223,7 @@ public AndConstraint BeEquivalentTo(TExpectation expe EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); var context = - new EquivalencyValidationContext(Node.From(() => AssertionScope.Current.CallerIdentity), options) + new EquivalencyValidationContext(Node.From(() => CurrentAssertionChain.CallerIdentifier), options) { Reason = new Reason(because, becauseArgs), TraceWriter = options.TraceWriter @@ -295,12 +298,12 @@ public AndConstraint ContainKeys(IEnumerable expected, ICollection expectedKeys = expected.ConvertOrCastToCollection(); Guard.ThrowIfArgumentIsEmpty(expectedKeys, nameof(expected), "Cannot verify key containment against an empty sequence"); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to contain keys {0}{reason}, but found .", expected); - if (success) + if (assertionChain.Succeeded) { IEnumerable missingKeys = expectedKeys.Where(key => !ContainsKey(Subject, key)); @@ -308,14 +311,15 @@ public AndConstraint ContainKeys(IEnumerable expected, { if (expectedKeys.Count > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:dictionary} {0} to contain keys {1}{reason}, but could not find {2}.", Subject, + .FailWith("Expected {context:dictionary} {0} to contain keys {1}{reason}, but could not find {2}.", + Subject, expected, missingKeys); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} to contain key {1}{reason}.", Subject, expected.First()); @@ -345,14 +349,14 @@ public AndConstraint ContainKeys(IEnumerable expected, public AndConstraint NotContainKey(TKey unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} not to contain key {0}{reason}, but found .", unexpected); - if (success && ContainsKey(Subject, unexpected)) + if (assertionChain.Succeeded && ContainsKey(Subject, unexpected)) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} not to contain key {1}{reason}, but found it anyhow.", Subject, unexpected); @@ -396,12 +400,12 @@ public AndConstraint NotContainKeys(IEnumerable unexpected, Guard.ThrowIfArgumentIsEmpty(unexpectedKeys, nameof(unexpected), "Cannot verify key containment against an empty sequence"); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to not contain keys {0}{reason}, but found .", unexpectedKeys); - if (success) + if (assertionChain.Succeeded) { IEnumerable foundKeys = unexpectedKeys.Where(key => ContainsKey(Subject, key)); @@ -409,14 +413,14 @@ public AndConstraint NotContainKeys(IEnumerable unexpected, { if (unexpectedKeys.Count > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} to not contain keys {1}{reason}, but found {2}.", Subject, unexpectedKeys, foundKeys); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} to not contain key {1}{reason}.", Subject, unexpectedKeys.First()); @@ -446,12 +450,10 @@ public AndConstraint NotContainKeys(IEnumerable unexpected, public AndWhichConstraint ContainValue(TValue expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - AndWhichConstraint> innerConstraint = - ContainValuesAndWhich([expected], because, becauseArgs); + AndWhichConstraint> result = + ContainValues([expected], because, becauseArgs); - return - new AndWhichConstraint( - innerConstraint.And, innerConstraint.Which); + return new AndWhichConstraint(result.And, result.Subject); } /// @@ -459,13 +461,13 @@ public AndWhichConstraint ContainValue(TValue expected, /// their implementation. /// /// The expected values - public AndConstraint ContainValues(params TValue[] expected) + public AndWhichConstraint> ContainValues(params TValue[] expected) { return ContainValues(expected, string.Empty); } /// - /// Asserts that the dictionary contains all of the specified values. Values are compared using + /// Asserts that the dictionary contains all the specified values. Values are compared using /// their implementation. /// /// The expected values @@ -478,68 +480,57 @@ public AndConstraint ContainValues(params TValue[] expected) /// /// is . /// is empty. - public AndConstraint ContainValues(IEnumerable expected, + public AndWhichConstraint> ContainValues(IEnumerable expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot verify value containment against a collection of values"); - return ContainValuesAndWhich(expected, because, becauseArgs); - } - - private AndWhichConstraint> ContainValuesAndWhich(IEnumerable expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) - { ICollection expectedValues = expected.ConvertOrCastToCollection(); Guard.ThrowIfArgumentIsEmpty(expectedValues, nameof(expected), "Cannot verify value containment against an empty sequence"); - bool success = Execute.Assertion + var missingValues = new List(expectedValues); + + Dictionary matches = new(); + + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:dictionary} to contain values {0}{reason}, but found {1}.", expected, Subject); - - IEnumerable matchedConstraint = null; + .FailWith("Expected {context:dictionary} to contain values {0}{reason}, but found .", expected); - if (success) + if (assertionChain.Succeeded) { - IEnumerable subjectValues = GetValues(Subject); - IEnumerable missingValues = expectedValues.Except(subjectValues); + foreach (var pair in Subject!) + { + if (missingValues.Contains(pair.Value)) + { + matches.Add(pair.Key, pair.Value); + missingValues.Remove(pair.Value); + } + } - if (missingValues.Any()) + if (missingValues.Count > 0) { - if (expectedValues.Count > 1) + if (expectedValues.Count == 1) { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:dictionary} {0} to contain value {1}{reason}, but could not find {2}.", Subject, - expected, missingValues); + assertionChain.FailWith( + "Expected {context:dictionary} {0} to contain value {1}{reason}.", + Subject, expectedValues.Single()); } else { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:dictionary} {0} to contain value {1}{reason}.", Subject, - expected.First()); + assertionChain.FailWith( + "Expected {context:dictionary} {0} to contain values {1}{reason}, but could not find {2}.", + Subject, expectedValues, missingValues.Count == 1 ? missingValues.Single() : missingValues); } } - - matchedConstraint = RepetitionPreservingIntersect(subjectValues, expectedValues); } - return new AndWhichConstraint>((TAssertions)this, matchedConstraint); - } + string postfix = matches.Count > 0 ? "[" + string.Join(" and ", matches.Keys) + "]" : ""; - /// - /// Returns an enumerable consisting of all items in the first collection also appearing in the second. - /// - /// Enumerable.Intersect is not suitable because it drops any repeated elements. - private static IEnumerable RepetitionPreservingIntersect( - IEnumerable first, IEnumerable second) - { - var secondSet = new HashSet(second); - return first.Where(e => secondSet.Contains(e)); + return new AndWhichConstraint>((TAssertions)this, matches.Values, assertionChain, postfix); } #endregion @@ -561,14 +552,14 @@ private static IEnumerable RepetitionPreservingIntersect( public AndConstraint NotContainValue(TValue unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} not to contain value {0}{reason}, but found .", unexpected); - if (success && GetValues(Subject).Contains(unexpected)) + if (assertionChain.Succeeded && GetValues(Subject).Contains(unexpected)) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} not to contain value {1}{reason}, but found it anyhow.", Subject, unexpected); @@ -612,12 +603,12 @@ public AndConstraint NotContainValues(IEnumerable unexpecte Guard.ThrowIfArgumentIsEmpty(unexpectedValues, nameof(unexpected), "Cannot verify value containment with an empty sequence"); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to not contain values {0}{reason}, but found .", unexpected); - if (success) + if (assertionChain.Succeeded) { IEnumerable foundValues = unexpectedValues.Intersect(GetValues(Subject)); @@ -625,14 +616,15 @@ public AndConstraint NotContainValues(IEnumerable unexpecte { if (unexpectedValues.Count > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:dictionary} {0} to not contain value {1}{reason}, but found {2}.", Subject, + .FailWith("Expected {context:dictionary} {0} to not contain value {1}{reason}, but found {2}.", + Subject, unexpected, foundValues); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} to not contain value {1}{reason}.", Subject, unexpected.First()); @@ -684,13 +676,13 @@ public AndConstraint Contain(params KeyValuePair[] ex Guard.ThrowIfArgumentIsEmpty(expectedKeyValuePairs, nameof(expected), "Cannot verify key containment against an empty collection of key/value pairs"); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to contain key/value pairs {0}{reason}, but dictionary is .", expected); - if (success) + if (assertionChain.Succeeded) { TKey[] expectedKeys = expectedKeyValuePairs.Select(keyValuePair => keyValuePair.Key).ToArray(); IEnumerable missingKeys = expectedKeys.Where(key => !ContainsKey(Subject, key)); @@ -699,7 +691,7 @@ public AndConstraint Contain(params KeyValuePair[] ex { if (expectedKeyValuePairs.Count > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} to contain key(s) {1}{reason}, but could not find keys {2}.", Subject, @@ -707,7 +699,7 @@ public AndConstraint Contain(params KeyValuePair[] ex } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} {0} to contain key {1}{reason}.", Subject, expectedKeys[0]); @@ -723,7 +715,7 @@ public AndConstraint Contain(params KeyValuePair[] ex { if (keyValuePairsNotSameOrEqualInSubject.Length > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:dictionary} to contain {0}{reason}, but {context:dictionary} differs at keys {1}.", @@ -734,7 +726,7 @@ public AndConstraint Contain(params KeyValuePair[] ex KeyValuePair expectedKeyValuePair = keyValuePairsNotSameOrEqualInSubject[0]; TValue actual = GetValue(Subject, expectedKeyValuePair.Key); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to contain value {0} at key {1}{reason}, but found {2}.", expectedKeyValuePair.Value, expectedKeyValuePair.Key, actual); @@ -782,19 +774,19 @@ public AndConstraint Contain(params KeyValuePair[] ex public AndConstraint Contain(TKey key, TValue value, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to contain value {0} at key {1}{reason}, but dictionary is .", value, key); - if (success) + if (assertionChain.Succeeded) { if (TryGetValue(Subject, key, out TValue actual)) { Func areSameOrEqual = ObjectExtensions.GetComparer(); - Execute.Assertion + assertionChain .ForCondition(areSameOrEqual(actual, value)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to contain value {0} at key {1}{reason}, but found {2}.", value, key, @@ -802,7 +794,7 @@ public AndConstraint Contain(TKey key, TValue value, } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to contain value {0} at key {1}{reason}, but the key was not found.", value, @@ -853,13 +845,13 @@ public AndConstraint NotContain(params KeyValuePair[] Guard.ThrowIfArgumentIsEmpty(keyValuePairs, nameof(items), "Cannot verify key containment against an empty collection of key/value pairs"); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} to not contain key/value pairs {0}{reason}, but dictionary is .", items); - if (success) + if (assertionChain.Succeeded) { KeyValuePair[] keyValuePairsFound = keyValuePairs.Where(keyValuePair => ContainsKey(Subject, keyValuePair.Key)).ToArray(); @@ -875,7 +867,7 @@ public AndConstraint NotContain(params KeyValuePair[] { if (keyValuePairsSameOrEqualInSubject.Length > 1) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:dictionary} to not contain key/value pairs {0}{reason}, but found them anyhow.", @@ -885,7 +877,7 @@ public AndConstraint NotContain(params KeyValuePair[] { KeyValuePair keyValuePair = keyValuePairsSameOrEqualInSubject[0]; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:dictionary} to not contain value {0} at key {1}{reason}, but found it anyhow.", @@ -935,15 +927,15 @@ public AndConstraint NotContain(params KeyValuePair[] public AndConstraint NotContain(TKey key, TValue value, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} not to contain value {0} at key {1}{reason}, but dictionary is .", value, key); - if (success && TryGetValue(Subject, key, out TValue actual)) + if (assertionChain.Succeeded && TryGetValue(Subject, key, out TValue actual)) { - Execute.Assertion + assertionChain .ForCondition(!ObjectExtensions.GetComparer()(actual, value)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:dictionary} not to contain value {0} at key {1}{reason}, but found it anyhow.", diff --git a/Src/FluentAssertions/Collections/StringCollectionAssertions.cs b/Src/FluentAssertions/Collections/StringCollectionAssertions.cs index 82e4498f3c..9229b4f846 100644 --- a/Src/FluentAssertions/Collections/StringCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/StringCollectionAssertions.cs @@ -13,8 +13,8 @@ public class StringCollectionAssertions : StringCollectionAssertions /// Initializes a new instance of the class. /// - public StringCollectionAssertions(IEnumerable actualValue) - : base(actualValue) + public StringCollectionAssertions(IEnumerable actualValue, AssertionChain assertionChain) + : base(actualValue, assertionChain) { } } @@ -26,8 +26,8 @@ public class StringCollectionAssertions /// /// Initializes a new instance of the class. /// - public StringCollectionAssertions(TCollection actualValue) - : base(actualValue) + public StringCollectionAssertions(TCollection actualValue, AssertionChain assertionChain) + : base(actualValue, assertionChain) { } } @@ -36,12 +36,15 @@ public class StringCollectionAssertions : GenericColle where TCollection : IEnumerable where TAssertions : StringCollectionAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public StringCollectionAssertions(TCollection actualValue) - : base(actualValue) + public StringCollectionAssertions(TCollection actualValue, AssertionChain assertionChain) + : base(actualValue, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -131,7 +134,7 @@ public AndConstraint BeEquivalentTo(IEnumerable expectation options = config(AssertionOptions.CloneDefaults()).AsCollection(); var context = - new EquivalencyValidationContext(Node.From>(() => AssertionScope.Current.CallerIdentity), options) + new EquivalencyValidationContext(Node.From>(() => CurrentAssertionChain.CallerIdentifier), options) { Reason = new Reason(because, becauseArgs), TraceWriter = options.TraceWriter @@ -248,45 +251,48 @@ public AndWhichConstraint ContainMatch(string wildcardPatte Guard.ThrowIfArgumentIsEmpty(wildcardPattern, nameof(wildcardPattern), "Cannot match strings in collection against an empty string. Provide a wildcard pattern or use the Contain method."); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:collection} to contain a match of {0}{reason}, but found .", wildcardPattern); - IEnumerable matched = []; + IEnumerable matches = []; + + int? firstMatch = null; - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + (matches, firstMatch) = AllThatMatch(wildcardPattern); + + assertionChain .BecauseOf(because, becauseArgs) - .ForCondition(ContainsMatch(wildcardPattern)) + .ForCondition(matches.Any()) .FailWith("Expected {context:collection} {0} to contain a match of {1}{reason}.", Subject, wildcardPattern); - - matched = AllThatMatch(wildcardPattern); } - return new AndWhichConstraint((TAssertions)this, matched); + return new AndWhichConstraint((TAssertions)this, matches, assertionChain, "[" + firstMatch + "]"); } - private bool ContainsMatch(string wildcardPattern) + private (string[] MatchingItems, int? FirstMatchingIndex) AllThatMatch(string wildcardPattern) { - using var scope = new AssertionScope(); - - return Subject.Any(item => - { - item.Should().Match(wildcardPattern); - return scope.Discard().Length == 0; - }); - } + int? firstMatchingIndex = null; - private IEnumerable AllThatMatch(string wildcardPattern) - { - return Subject.Where(item => + var matches = Subject.Where((item, index) => { using var scope = new AssertionScope(); + item.Should().Match(wildcardPattern); - return scope.Discard().Length == 0; + + if (scope.Discard().Length == 0) + { + firstMatchingIndex ??= index; + return true; + } + + return false; }); + + return (matches.ToArray(), firstMatchingIndex); } /// @@ -333,15 +339,15 @@ public AndConstraint NotContainMatch(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."); - bool success = Execute.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 .", wildcardPattern); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(NotContainsMatch(wildcardPattern)) .FailWith("Did not expect {context:collection} {0} to contain a match of {1}{reason}.", Subject, wildcardPattern); diff --git a/Src/FluentAssertions/Collections/SubsequentOrderingAssertions.cs b/Src/FluentAssertions/Collections/SubsequentOrderingAssertions.cs index 8d1faebf3b..82634696b1 100644 --- a/Src/FluentAssertions/Collections/SubsequentOrderingAssertions.cs +++ b/Src/FluentAssertions/Collections/SubsequentOrderingAssertions.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using FluentAssertions.Execution; namespace FluentAssertions.Collections; @@ -8,8 +9,8 @@ namespace FluentAssertions.Collections; public class SubsequentOrderingAssertions : SubsequentOrderingGenericCollectionAssertions, T, SubsequentOrderingAssertions> { - public SubsequentOrderingAssertions(IEnumerable actualValue, IOrderedEnumerable previousOrderedEnumerable) - : base(actualValue, previousOrderedEnumerable) + public SubsequentOrderingAssertions(IEnumerable actualValue, IOrderedEnumerable previousOrderedEnumerable, AssertionChain assertionChain) + : base(actualValue, previousOrderedEnumerable, assertionChain) { } } diff --git a/Src/FluentAssertions/Collections/SubsequentOrderingGenericCollectionAssertions.cs b/Src/FluentAssertions/Collections/SubsequentOrderingGenericCollectionAssertions.cs index 104dedac65..789176f263 100644 --- a/Src/FluentAssertions/Collections/SubsequentOrderingGenericCollectionAssertions.cs +++ b/Src/FluentAssertions/Collections/SubsequentOrderingGenericCollectionAssertions.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Linq.Expressions; using FluentAssertions.Common; +using FluentAssertions.Execution; namespace FluentAssertions.Collections; @@ -17,8 +18,8 @@ public class SubsequentOrderingGenericCollectionAssertions previousOrderedEnumerable; private bool subsequentOrdering; - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable previousOrderedEnumerable) - : base(actualValue) + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable previousOrderedEnumerable, AssertionChain assertionChain) + : base(actualValue, assertionChain) { this.previousOrderedEnumerable = previousOrderedEnumerable; } @@ -170,8 +171,8 @@ public class SubsequentOrderingGenericCollectionAssertions : SubsequentOrderingGenericCollectionAssertions> where TCollection : IEnumerable { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable previousOrderedEnumerable) - : base(actualValue, previousOrderedEnumerable) + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, IOrderedEnumerable previousOrderedEnumerable, AssertionChain assertionChain) + : base(actualValue, previousOrderedEnumerable, assertionChain) { } } diff --git a/Src/FluentAssertions/Common/MethodInfoExtensions.cs b/Src/FluentAssertions/Common/MethodInfoExtensions.cs index 7fcef498a5..5219f0570b 100644 --- a/Src/FluentAssertions/Common/MethodInfoExtensions.cs +++ b/Src/FluentAssertions/Common/MethodInfoExtensions.cs @@ -30,7 +30,6 @@ internal static IEnumerable GetMatchingAttributes(this M if (typeof(TAttribute) == typeof(MethodImplAttribute) && memberInfo is MethodBase methodBase) { (bool success, MethodImplAttribute methodImplAttribute) = RecreateMethodImplAttribute(methodBase); - if (success) { customAttributes.Add(methodImplAttribute as TAttribute); diff --git a/Src/FluentAssertions/Common/ObjectExtensions.cs b/Src/FluentAssertions/Common/ObjectExtensions.cs index 7ba5e3bdcc..e1bf0f0032 100644 --- a/Src/FluentAssertions/Common/ObjectExtensions.cs +++ b/Src/FluentAssertions/Common/ObjectExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using FluentAssertions.Formatting; namespace FluentAssertions.Common; @@ -77,4 +78,12 @@ ushort or uint or ulong); } + + /// + /// Convenience method to format an object to a string using the class. + /// + public static string ToFormattedString(this object source) + { + return Formatter.ToString(source); + } } diff --git a/Src/FluentAssertions/Common/StringExtensions.cs b/Src/FluentAssertions/Common/StringExtensions.cs index e83ab55b24..f1b047d02f 100644 --- a/Src/FluentAssertions/Common/StringExtensions.cs +++ b/Src/FluentAssertions/Common/StringExtensions.cs @@ -127,6 +127,12 @@ public static string RemoveNewlineStyle(this string @this) .Replace("\r", "\n", StringComparison.Ordinal); } + public static string RemoveTrailingWhitespaceFromLines(this string input) + { + // This regex matches whitespace characters (\s) that are followed by a line ending (\r?\n) + return Regex.Replace(input, @"[ \t]+(?=\r?\n)", string.Empty); + } + /// /// Counts the number of times the appears within a string by using the specified . /// @@ -156,4 +162,9 @@ public static bool IsLongOrMultiline(this string value) const int humanReadableLength = 8; return value.Length > humanReadableLength || value.Contains(Environment.NewLine, StringComparison.Ordinal); } + + public static bool IsNullOrEmpty(this string value) + { + return string.IsNullOrEmpty(value); + } } diff --git a/Src/FluentAssertions/CustomAssertionAttribute.cs b/Src/FluentAssertions/CustomAssertionAttribute.cs index 6078c1457f..87dfa929b1 100644 --- a/Src/FluentAssertions/CustomAssertionAttribute.cs +++ b/Src/FluentAssertions/CustomAssertionAttribute.cs @@ -4,7 +4,7 @@ namespace FluentAssertions; /// /// Marks a method as an extension to Fluent Assertions that either uses the built-in assertions -/// internally, or directly uses the Execute.Assertion. +/// internally, or directly uses AssertionChain. /// [AttributeUsage(AttributeTargets.Method)] #pragma warning disable CA1813 // Avoid unsealed attributes. This type has shipped. diff --git a/Src/FluentAssertions/CustomAssertionsAssemblyAttribute.cs b/Src/FluentAssertions/CustomAssertionsAssemblyAttribute.cs index faaff8e2b3..9e0cc90410 100644 --- a/Src/FluentAssertions/CustomAssertionsAssemblyAttribute.cs +++ b/Src/FluentAssertions/CustomAssertionsAssemblyAttribute.cs @@ -4,7 +4,7 @@ namespace FluentAssertions; /// /// Marks an assembly as containing extensions to Fluent Assertions that either uses the built-in assertions -/// internally, or directly uses the Execute.Assertion. +/// internally, or directly uses AssertionChain. /// [AttributeUsage(AttributeTargets.Assembly)] public sealed class CustomAssertionsAssemblyAttribute : Attribute; diff --git a/Src/FluentAssertions/EnumAssertionsExtensions.cs b/Src/FluentAssertions/EnumAssertionsExtensions.cs index 31688fb9cc..da15e34440 100644 --- a/Src/FluentAssertions/EnumAssertionsExtensions.cs +++ b/Src/FluentAssertions/EnumAssertionsExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Diagnostics.Contracts; +using FluentAssertions.Execution; using FluentAssertions.Primitives; namespace FluentAssertions; @@ -19,7 +20,7 @@ public static class EnumAssertionsExtensions public static EnumAssertions Should(this TEnum @enum) where TEnum : struct, Enum { - return new EnumAssertions(@enum); + return new EnumAssertions(@enum, AssertionChain.GetOrCreate()); } /// @@ -30,6 +31,6 @@ public static EnumAssertions Should(this TEnum @enum) public static NullableEnumAssertions Should(this TEnum? @enum) where TEnum : struct, Enum { - return new NullableEnumAssertions(@enum); + return new NullableEnumAssertions(@enum, AssertionChain.GetOrCreate()); } } diff --git a/Src/FluentAssertions/Equivalency/AssertionChainExtensions.cs b/Src/FluentAssertions/Equivalency/AssertionChainExtensions.cs new file mode 100644 index 0000000000..95227db2cb --- /dev/null +++ b/Src/FluentAssertions/Equivalency/AssertionChainExtensions.cs @@ -0,0 +1,19 @@ +using FluentAssertions.Execution; + +namespace FluentAssertions.Equivalency; + +internal static class AssertionChainExtensions +{ + /// + /// Updates the with the relevant information from the current , including the correct + /// caller identification path. + /// + public static AssertionChain For(this AssertionChain chain, IEquivalencyValidationContext context) + { + chain.OverrideCallerIdentifier(() => context.CurrentNode.Description); + + return chain + .WithReportable("configuration", () => context.Options.ToString()) + .BecauseOf(context.Reason); + } +} diff --git a/Src/FluentAssertions/Equivalency/EquivalencyStep.cs b/Src/FluentAssertions/Equivalency/EquivalencyStep.cs index d50c77023a..58f689f771 100644 --- a/Src/FluentAssertions/Equivalency/EquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/EquivalencyStep.cs @@ -20,5 +20,5 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon /// Implements , but only gets called when the expected type matches . /// protected abstract EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, - IValidateChildNodeEquivalency nested); + IValidateChildNodeEquivalency nestedValidator); } diff --git a/Src/FluentAssertions/Equivalency/EquivalencyValidationContext.cs b/Src/FluentAssertions/Equivalency/EquivalencyValidationContext.cs index 9d7215a971..2fbced998e 100644 --- a/Src/FluentAssertions/Equivalency/EquivalencyValidationContext.cs +++ b/Src/FluentAssertions/Equivalency/EquivalencyValidationContext.cs @@ -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; } diff --git a/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs index 6d7de0a3fd..b856f6d22a 100644 --- a/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/EquivalencyValidator.cs @@ -15,10 +15,6 @@ public void AssertEquality(Comparands comparands, EquivalencyValidationContext c { using var scope = new AssertionScope(); - scope.AssumeSingleCaller(); - scope.AddReportable("configuration", () => context.Options.ToString()); - scope.BecauseOf(context.Reason); - RecursivelyAssertEquivalencyOf(comparands, context); if (context.TraceWriter is not null) @@ -34,39 +30,40 @@ private void RecursivelyAssertEquivalencyOf(Comparands comparands, IEquivalencyV public void AssertEquivalencyOf(Comparands comparands, IEquivalencyValidationContext context) { - var scope = AssertionScope.Current; + var assertionChain = AssertionChain.GetOrCreate() + .For(context) + .BecauseOf(context.Reason); - if (ShouldContinueThisDeep(context.CurrentNode, context.Options, scope)) + if (ShouldContinueThisDeep(context.CurrentNode, context.Options, assertionChain)) { - TrackWhatIsNeededToProvideContextToFailures(scope, comparands, context.CurrentNode); - if (!context.IsCyclicReference(comparands.Expectation)) { TryToProveNodesAreEquivalent(comparands, context); } + else if (context.Options.CyclicReferenceHandling == CyclicReferenceHandling.ThrowException) + { + assertionChain.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, - AssertionScope assertionScope) + AssertionChain assertionChain) { bool shouldRecurse = options.AllowInfiniteRecursion || currentNode.Depth <= MaxDepth; if (!shouldRecurse) { // This will throw, unless we're inside an AssertionScope - assertionScope.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(AssertionScope scope, Comparands comparands, INode currentNode) - { - scope.Context = new Lazy(() => currentNode.Description); - - scope.TrackComparands(comparands.Subject, comparands.Expectation); - } - private void TryToProveNodesAreEquivalent(Comparands comparands, IEquivalencyValidationContext context) { using var _ = context.Tracer.WriteBlock(node => node.Description); diff --git a/Src/FluentAssertions/Equivalency/Execution/CyclicReferenceDetector.cs b/Src/FluentAssertions/Equivalency/Execution/CyclicReferenceDetector.cs index 600823aee9..4ca13ce49d 100644 --- a/Src/FluentAssertions/Equivalency/Execution/CyclicReferenceDetector.cs +++ b/Src/FluentAssertions/Equivalency/Execution/CyclicReferenceDetector.cs @@ -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. /// - /// - /// The behavior of a cyclic reference is determined by the parameter. - /// - 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; diff --git a/Src/FluentAssertions/Equivalency/IEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/IEquivalencyStep.cs index d4f744b5ea..d0279009ae 100644 --- a/Src/FluentAssertions/Equivalency/IEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/IEquivalencyStep.cs @@ -15,5 +15,6 @@ public interface IEquivalencyStep /// /// May throw when preconditions are not met or if it detects mismatching data. /// - EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IValidateChildNodeEquivalency valueChildNodes); + EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, + IValidateChildNodeEquivalency valueChildNodes); } diff --git a/Src/FluentAssertions/Equivalency/IMemberMatchingRule.cs b/Src/FluentAssertions/Equivalency/IMemberMatchingRule.cs index 00f1dbcdad..0a59d796eb 100644 --- a/Src/FluentAssertions/Equivalency/IMemberMatchingRule.cs +++ b/Src/FluentAssertions/Equivalency/IMemberMatchingRule.cs @@ -1,3 +1,5 @@ +using FluentAssertions.Execution; + namespace FluentAssertions.Equivalency; /// @@ -15,17 +17,18 @@ public interface IMemberMatchingRule /// simply return . /// /// - /// The of the subject's member for which a match must be found. Can never - /// be . + /// The of the subject's member for which a match must be found. Can never + /// be . /// /// - /// The subject object for which a matching member must be returned. Can never be . + /// The subject object for which a matching member must be returned. Can never be . /// /// /// + /// /// /// Returns the of the property with which to compare the subject with, or /// if no match was found. /// - IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options); + IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, AssertionChain assertionChain); } diff --git a/Src/FluentAssertions/Equivalency/Matching/MappedMemberMatchingRule.cs b/Src/FluentAssertions/Equivalency/Matching/MappedMemberMatchingRule.cs index 51b6c43a2b..faa1aaaea2 100644 --- a/Src/FluentAssertions/Equivalency/Matching/MappedMemberMatchingRule.cs +++ b/Src/FluentAssertions/Equivalency/Matching/MappedMemberMatchingRule.cs @@ -1,6 +1,7 @@ using System; using System.Text.RegularExpressions; using FluentAssertions.Common; +using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Matching; @@ -29,7 +30,7 @@ public MappedMemberMatchingRule(string expectationMemberName, string subjectMemb this.subjectMemberName = subjectMemberName; } - public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options) + 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) diff --git a/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs b/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs index ded3bcaa9f..88c44a8f4c 100644 --- a/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs +++ b/Src/FluentAssertions/Equivalency/Matching/MappedPathMatchingRule.cs @@ -1,5 +1,6 @@ using System; using FluentAssertions.Common; +using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Matching; @@ -42,7 +43,7 @@ public MappedPathMatchingRule(string expectationMemberPath, string subjectMember } } - public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options) + public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, AssertionChain assertionChain) { MemberPath path = expectationPath; diff --git a/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs b/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs index 36d220c195..dc5ac17c6e 100644 --- a/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs +++ b/Src/FluentAssertions/Equivalency/Matching/MustMatchByNameRule.cs @@ -9,7 +9,7 @@ namespace FluentAssertions.Equivalency.Matching; /// internal class MustMatchByNameRule : IMemberMatchingRule { - public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options) + public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, AssertionChain assertionChain) { IMember subjectMember = null; @@ -33,12 +33,12 @@ public IMember Match(IMember expectedMember, object subject, INode parent, IEqui if (subjectMember is null) { - Execute.Assertion.FailWith( + assertionChain.FailWith( $"Expectation has {expectedMember.Description} that the other object does not have."); } else if (options.IgnoreNonBrowsableOnSubject && !subjectMember.IsBrowsable) { - Execute.Assertion.FailWith( + assertionChain.FailWith( $"Expectation has {expectedMember.Description} that is non-browsable in the other object, and non-browsable " + "members on the subject are ignored with the current configuration"); } diff --git a/Src/FluentAssertions/Equivalency/Matching/TryMatchByNameRule.cs b/Src/FluentAssertions/Equivalency/Matching/TryMatchByNameRule.cs index 06cabfdf60..9d6d7dc95c 100644 --- a/Src/FluentAssertions/Equivalency/Matching/TryMatchByNameRule.cs +++ b/Src/FluentAssertions/Equivalency/Matching/TryMatchByNameRule.cs @@ -1,5 +1,6 @@ using System.Reflection; using FluentAssertions.Common; +using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Matching; @@ -8,7 +9,7 @@ namespace FluentAssertions.Equivalency.Matching; /// internal class TryMatchByNameRule : IMemberMatchingRule { - public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options) + public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, AssertionChain assertionChain) { if (options.IncludedProperties != MemberVisibility.None) { diff --git a/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs index 016a566003..4962f36860 100644 --- a/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/MultiDimensionalArrayEquivalencyStep.cs @@ -18,7 +18,7 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.ContinueWithNext; } - if (AreComparable(comparands, expectationAsArray)) + if (AreComparable(comparands, expectationAsArray, AssertionChain.GetOrCreate().For(context))) { if (expectationAsArray.Length == 0) { @@ -36,7 +36,8 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon IEquivalencyValidationContext itemContext = context.AsCollectionItem(listOfIndices); - valueChildNodes.AssertEquivalencyOf(new Comparands(subject, expectation, typeof(object)), itemContext); + valueChildNodes.AssertEquivalencyOf(new Comparands(subject, expectation, typeof(object)), + itemContext); } while (digit.Increment()); } @@ -52,25 +53,27 @@ private static Digit BuildDigitsRepresentingAllIndices(Array subjectAsArray) .Aggregate((Digit)null, (next, rank) => new Digit(subjectAsArray.GetLength(rank), next)); } - private static bool AreComparable(Comparands comparands, Array expectationAsArray) + private static bool AreComparable(Comparands comparands, Array expectationAsArray, AssertionChain assertionChain) { return - IsArray(comparands.Subject) && - HaveSameRank(comparands.Subject, expectationAsArray) && - HaveSameDimensions(comparands.Subject, expectationAsArray); + IsArray(comparands.Subject, assertionChain) && + HaveSameRank(comparands.Subject, expectationAsArray, assertionChain) && + HaveSameDimensions(comparands.Subject, expectationAsArray, assertionChain); } - private static bool IsArray(object type) + private static bool IsArray(object type, AssertionChain assertionChain) { - return AssertionScope.Current + assertionChain .ForCondition(type is not null) .FailWith("Cannot compare a multi-dimensional array to .") .Then .ForCondition(type is Array) .FailWith("Cannot compare a multi-dimensional array to something else."); + + return assertionChain.Succeeded; } - private static bool HaveSameDimensions(object subject, Array expectation) + private static bool HaveSameDimensions(object subject, Array expectation, AssertionChain assertionChain) { bool sameDimensions = true; @@ -79,22 +82,26 @@ private static bool HaveSameDimensions(object subject, Array expectation) int actualLength = ((Array)subject).GetLength(dimension); int expectedLength = expectation.GetLength(dimension); - sameDimensions &= AssertionScope.Current + assertionChain .ForCondition(expectedLength == actualLength) .FailWith("Expected dimension {0} to contain {1} item(s), but found {2}.", dimension, expectedLength, actualLength); + + sameDimensions &= assertionChain.Succeeded; } return sameDimensions; } - private static bool HaveSameRank(object subject, Array expectation) + private static bool HaveSameRank(object subject, Array expectation, AssertionChain assertionChain) { var subjectAsArray = (Array)subject; - return AssertionScope.Current + assertionChain .ForCondition(subjectAsArray.Rank == expectation.Rank) .FailWith("Expected {context:array} to have {0} dimension(s), but it has {1}.", expectation.Rank, subjectAsArray.Rank); + + return assertionChain.Succeeded; } } diff --git a/Src/FluentAssertions/Equivalency/Node.cs b/Src/FluentAssertions/Equivalency/Node.cs index 4f264a3d38..96ee51e87f 100644 --- a/Src/FluentAssertions/Equivalency/Node.cs +++ b/Src/FluentAssertions/Equivalency/Node.cs @@ -10,11 +10,18 @@ internal class Node : INode { private static readonly Regex MatchFirstIndex = new(@"^\[[0-9]+\]$"); + private GetSubjectId subjectIdProvider; + private string path; private string name; private string pathAndName; + private string cachedSubjectId; - public GetSubjectId GetSubjectId { get; protected set; } = () => string.Empty; + public GetSubjectId GetSubjectId + { + get => () => cachedSubjectId ??= subjectIdProvider(); + protected init => subjectIdProvider = value; + } public Type Type { get; protected set; } @@ -76,7 +83,7 @@ public static INode From(GetSubjectId getSubjectId) { return new Node { - GetSubjectId = () => getSubjectId() ?? "root", + subjectIdProvider = () => getSubjectId() ?? "root", Name = string.Empty, Path = string.Empty, Type = typeof(T), diff --git a/Src/FluentAssertions/Equivalency/Steps/AssertionResultSet.cs b/Src/FluentAssertions/Equivalency/Steps/AssertionResultSet.cs index b1b9b7cfe2..8bde0c02aa 100644 --- a/Src/FluentAssertions/Equivalency/Steps/AssertionResultSet.cs +++ b/Src/FluentAssertions/Equivalency/Steps/AssertionResultSet.cs @@ -22,7 +22,7 @@ public void AddSet(object key, string[] failures) } /// - /// Returns the closest match compared to the set identified by the provided or + /// Returns the closest match compared to the set identified by the provided or /// an empty array if one of the results represents a successful assertion. /// /// diff --git a/Src/FluentAssertions/Equivalency/Steps/AssertionRuleEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/AssertionRuleEquivalencyStep.cs index ce2116a0fe..2a1990786b 100644 --- a/Src/FluentAssertions/Equivalency/Steps/AssertionRuleEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/AssertionRuleEquivalencyStep.cs @@ -10,15 +10,15 @@ public class AssertionRuleEquivalencyStep : IEquivalencyStep { private readonly Func predicate; private readonly string description; - private readonly Action> assertion; + private readonly Action> assertionAction; private readonly AutoConversionStep converter = new(); public AssertionRuleEquivalencyStep( Expression> predicate, - Action> assertion) + Action> assertionAction) { this.predicate = predicate.Compile(); - this.assertion = assertion; + this.assertionAction = assertionAction; description = predicate.ToString(); } @@ -49,7 +49,6 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon { // Try again after conversion success = ExecuteAssertion(comparands, context); - if (success) { // If the assertion succeeded after conversion, discard the failures from @@ -67,30 +66,33 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon private bool ExecuteAssertion(Comparands comparands, IEquivalencyValidationContext context) { bool subjectIsNull = comparands.Subject is null; + bool expectationIsNull = comparands.Expectation is null; - bool subjectIsValidType = - AssertionScope.Current + var assertionChain = AssertionChain.GetOrCreate().For(context); + + assertionChain .ForCondition(subjectIsNull || comparands.Subject.GetType().IsSameOrInherits(typeof(TSubject))) .FailWith("Expected " + context.CurrentNode.Description + " from subject to be a {0}{reason}, but found a {1}.", - typeof(TSubject), comparands.Subject?.GetType()); - - bool expectationIsNull = comparands.Expectation is null; - - bool expectationIsValidType = - AssertionScope.Current + typeof(TSubject), comparands.Subject?.GetType()) + .Then .ForCondition(expectationIsNull || comparands.Expectation.GetType().IsSameOrInherits(typeof(TSubject))) .FailWith( "Expected " + context.CurrentNode.Description + " from expectation to be a {0}{reason}, but found a {1}.", typeof(TSubject), comparands.Expectation?.GetType()); - if (subjectIsValidType && expectationIsValidType) + if (assertionChain.Succeeded) { if ((subjectIsNull || expectationIsNull) && !CanBeNull()) { return false; } - assertion(AssertionContext.CreateFrom(comparands, context)); + // Caller identitification should not get confused about invoking a Should within the assertion action + string callerIdentifier = context.CurrentNode.Description; + assertionChain.OverrideCallerIdentifier(() => callerIdentifier); + assertionChain.ReuseOnce(); + + assertionAction(AssertionContext.CreateFrom(comparands, context)); return true; } diff --git a/Src/FluentAssertions/Equivalency/Steps/DictionaryEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/DictionaryEquivalencyStep.cs index 67dff5dbf2..3aef3ec044 100644 --- a/Src/FluentAssertions/Equivalency/Steps/DictionaryEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/DictionaryEquivalencyStep.cs @@ -1,6 +1,8 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; +using FluentAssertions.Common; using FluentAssertions.Execution; +using FluentAssertions.Formatting; using static System.FormattableString; namespace FluentAssertions.Equivalency.Steps; @@ -8,13 +10,16 @@ namespace FluentAssertions.Equivalency.Steps; public class DictionaryEquivalencyStep : EquivalencyStep { [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, - IValidateChildNodeEquivalency nested) + protected override EquivalencyResult OnHandle(Comparands comparands, + IEquivalencyValidationContext context, + IValidateChildNodeEquivalency nestedValidator) { var subject = comparands.Subject as IDictionary; var expectation = comparands.Expectation as IDictionary; - if (PreconditionsAreMet(expectation, subject) && expectation is not null) + var assertionChain = AssertionChain.GetOrCreate().For(context); + + if (PreconditionsAreMet(expectation, subject, assertionChain) && expectation is not null) { foreach (object key in expectation.Keys) { @@ -23,8 +28,7 @@ protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalenc context.Tracer.WriteLine(member => Invariant($"Recursing into dictionary item {key} at {member.Description}")); - nested.AssertEquivalencyOf(new Comparands(subject[key], expectation[key], typeof(object)), - context.AsDictionaryItem(key)); + nestedValidator.AssertEquivalencyOf(new Comparands(subject[key], expectation[key], typeof(object)), context.AsDictionaryItem(key)); } else { @@ -32,6 +36,7 @@ protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalenc Invariant( $"Comparing dictionary item {key} at {member.Description} between subject and expectation")); + assertionChain.WithCallerPostfix($"[{key.ToFormattedString()}]").ReuseOnce(); subject[key].Should().Be(expectation[key], context.Reason.FormattedMessage, context.Reason.Arguments); } } @@ -40,32 +45,38 @@ protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalenc return EquivalencyResult.EquivalencyProven; } - private static bool PreconditionsAreMet(IDictionary expectation, IDictionary subject) + private static bool PreconditionsAreMet(IDictionary expectation, IDictionary subject, AssertionChain assertionChain) { - return AssertIsDictionary(subject) - && AssertEitherIsNotNull(expectation, subject) - && AssertSameLength(expectation, subject); + return AssertIsDictionary(subject, assertionChain) + && AssertEitherIsNotNull(expectation, subject, assertionChain) + && AssertSameLength(expectation, subject, assertionChain); } - private static bool AssertEitherIsNotNull(IDictionary expectation, IDictionary subject) + private static bool AssertEitherIsNotNull(IDictionary expectation, IDictionary subject, AssertionChain assertionChain) { - return AssertionScope.Current + assertionChain .ForCondition((expectation is null && subject is null) || expectation is not null) .FailWith("Expected {context:subject} to be {0}{reason}, but found {1}.", null, subject); + + return assertionChain.Succeeded; } - private static bool AssertIsDictionary(IDictionary subject) + private static bool AssertIsDictionary(IDictionary subject, AssertionChain assertionChain) { - return AssertionScope.Current + assertionChain .ForCondition(subject is not null) .FailWith("Expected {context:subject} to be a dictionary, but it is not."); + + return assertionChain.Succeeded; } - private static bool AssertSameLength(IDictionary expectation, IDictionary subject) + private static bool AssertSameLength(IDictionary expectation, IDictionary subject, AssertionChain assertionChain) { - return AssertionScope.Current + assertionChain .ForCondition(expectation is null || subject.Keys.Count == expectation.Keys.Count) .FailWith("Expected {context:subject} to be a dictionary with {0} item(s), but it only contains {1} item(s).", expectation?.Keys.Count, subject?.Keys.Count); + + return assertionChain.Succeeded; } } diff --git a/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs b/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs index f24ac8a726..e0ca76da14 100644 --- a/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs +++ b/Src/FluentAssertions/Equivalency/Steps/DictionaryInterfaceInfo.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Reflection; using FluentAssertions.Common; -using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Steps; @@ -73,11 +72,8 @@ public static DictionaryInterfaceInfo FindFromWithKey(Type target, string role, if (suitableDictionaryInterfaces.Length > 1) { - // SMELL: Code could be written to handle this better, but is it really worth the effort? - AssertionScope.Current.FailWith( + throw new InvalidOperationException( $"The {role} implements multiple IDictionary interfaces taking a key of {key}. "); - - return null; } if (suitableDictionaryInterfaces.Length == 0) diff --git a/Src/FluentAssertions/Equivalency/Steps/EnumEqualityStep.cs b/Src/FluentAssertions/Equivalency/Steps/EnumEqualityStep.cs index a0251192ca..a089bdc445 100644 --- a/Src/FluentAssertions/Equivalency/Steps/EnumEqualityStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/EnumEqualityStep.cs @@ -18,7 +18,9 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.ContinueWithNext; } - bool succeeded = Execute.Assertion + var assertionChain = AssertionChain.GetOrCreate().For(context); + + assertionChain .ForCondition(comparands.Subject?.GetType().IsEnum == true) .BecauseOf(context.Reason) .FailWith(() => @@ -31,16 +33,16 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon comparands.Subject); }); - if (succeeded) + if (assertionChain.Succeeded) { switch (context.Options.EnumEquivalencyHandling) { case EnumEquivalencyHandling.ByValue: - HandleByValue(comparands, context.Reason); + HandleByValue(assertionChain, comparands, context.Reason); break; case EnumEquivalencyHandling.ByName: - HandleByName(comparands, context.Reason); + HandleByName(assertionChain, comparands, context.Reason); break; default: @@ -51,12 +53,12 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.EquivalencyProven; } - private static void HandleByValue(Comparands comparands, Reason reason) + private static void HandleByValue(AssertionChain assertionChain, Comparands comparands, Reason reason) { decimal? subjectsUnderlyingValue = ExtractDecimal(comparands.Subject); decimal? expectationsUnderlyingValue = ExtractDecimal(comparands.Expectation); - Execute.Assertion + assertionChain .ForCondition(subjectsUnderlyingValue == expectationsUnderlyingValue) .BecauseOf(reason) .FailWith(() => @@ -69,12 +71,12 @@ private static void HandleByValue(Comparands comparands, Reason reason) }); } - private static void HandleByName(Comparands comparands, Reason reason) + private static void HandleByName(AssertionChain assertionChain, Comparands comparands, Reason reason) { string subject = comparands.Subject.ToString(); string expected = comparands.Expectation.ToString(); - Execute.Assertion + assertionChain .ForCondition(subject == expected) .BecauseOf(reason) .FailWith(() => diff --git a/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyStep.cs index 45fc7d57e1..1b6906ba58 100644 --- a/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyStep.cs @@ -15,9 +15,11 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.ContinueWithNext; } - if (AssertSubjectIsCollection(comparands.Subject)) + var assertionChain = AssertionChain.GetOrCreate().For(context); + + if (AssertSubjectIsCollection(assertionChain, comparands.Subject)) { - var validator = new EnumerableEquivalencyValidator(valueChildNodes, context) + var validator = new EnumerableEquivalencyValidator(assertionChain, valueChildNodes, context) { Recursive = context.CurrentNode.IsRoot || context.Options.IsRecursive, OrderingRules = context.Options.OrderingRules @@ -29,20 +31,20 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.EquivalencyProven; } - private static bool AssertSubjectIsCollection(object subject) + private static bool AssertSubjectIsCollection(AssertionChain assertionChain, object subject) { - bool conditionMet = AssertionScope.Current + assertionChain .ForCondition(subject is not null) .FailWith("Expected a collection, but {context:Subject} is ."); - if (conditionMet) + if (assertionChain.Succeeded) { - conditionMet = AssertionScope.Current + assertionChain .ForCondition(IsCollection(subject.GetType())) .FailWith("Expected a collection, but {context:Subject} is of a non-collection type."); } - return conditionMet; + return assertionChain.Succeeded; } private static bool IsCollection(Type type) diff --git a/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidator.cs b/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidator.cs index 3a58151a4c..c2e7d5640f 100644 --- a/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidator.cs +++ b/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidator.cs @@ -16,13 +16,16 @@ internal class EnumerableEquivalencyValidator #region Private Definitions + private readonly AssertionChain assertionChain; private readonly IValidateChildNodeEquivalency parent; private readonly IEquivalencyValidationContext context; #endregion - public EnumerableEquivalencyValidator(IValidateChildNodeEquivalency parent, IEquivalencyValidationContext context) + public EnumerableEquivalencyValidator(AssertionChain assertionChain, IValidateChildNodeEquivalency parent, + IEquivalencyValidationContext context) { + this.assertionChain = assertionChain; this.parent = parent; this.context = context; Recursive = false; @@ -54,24 +57,25 @@ public void Execute(object[] subject, T[] expectation) } } - private static bool AssertIsNotNull(object expectation, object[] subject) + private bool AssertIsNotNull(object expectation, object[] subject) { - return AssertionScope.Current + assertionChain .ForCondition(expectation is not null) - .FailWith("Expected {context:subject} to be , but found {0}.", new object[] { subject }); + .FailWith("Expected {context:subject} to be , but found {0}.", [subject]); + + return assertionChain.Succeeded; } - private static Continuation AssertCollectionsHaveSameCount(ICollection subject, ICollection expectation) + private bool AssertCollectionsHaveSameCount(ICollection subject, ICollection expectation) { - return AssertionScope.Current - .WithExpectation("Expected {context:subject} to be a collection with {0} item(s){reason}", expectation.Count) + assertionChain .AssertEitherCollectionIsNotEmpty(subject, expectation) .Then .AssertCollectionHasEnoughItems(subject, expectation) .Then - .AssertCollectionHasNotTooManyItems(subject, expectation) - .Then - .ClearExpectation(); + .AssertCollectionHasNotTooManyItems(subject, expectation); + + return assertionChain.Succeeded; } private void AssertElementGraphEquivalency(object[] subjects, T[] expectations, INode currentNode) @@ -101,11 +105,9 @@ private void AssertElementGraphEquivalencyWithStrictOrdering(object[] subject $"Strictly comparing expectation {expectation} at {member.Description} to item with index {index} in {subjects}")); bool succeeded = StrictlyMatchAgainst(subjects, expectation, index); - if (!succeeded) { failedCount++; - if (failedCount >= FailedItemsFastFailThreshold) { context.Tracer.WriteLine(member => @@ -185,7 +187,7 @@ private bool LooselyMatchAgainst(IList subjects, T expectation, int e foreach (string failure in results.GetTheFailuresForTheSetWithTheFewestFailures(expectationIndex)) { - AssertionScope.Current.AddPreFormattedFailure(failure); + assertionChain.AddPreFormattedFailure(failure); } return indexToBeRemoved != -1; @@ -195,8 +197,7 @@ private string[] TryToMatch(object subject, T expectation, int expectationInd { using var scope = new AssertionScope(); - parent.AssertEquivalencyOf(new Comparands(subject, expectation, typeof(T)), - context.AsCollectionItem(expectationIndex)); + parent.AssertEquivalencyOf(new Comparands(subject, expectation, typeof(T)), context.AsCollectionItem(expectationIndex)); return scope.Discard(); } diff --git a/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidatorExtensions.cs b/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidatorExtensions.cs index 473811c746..f3f3d00e21 100644 --- a/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidatorExtensions.cs +++ b/Src/FluentAssertions/Equivalency/Steps/EnumerableEquivalencyValidatorExtensions.cs @@ -6,38 +6,46 @@ namespace FluentAssertions.Equivalency.Steps; internal static class EnumerableEquivalencyValidatorExtensions { - public static Continuation AssertEitherCollectionIsNotEmpty(this IAssertionScope scope, ICollection subject, + public static Continuation AssertEitherCollectionIsNotEmpty(this AssertionChain assertionChain, + ICollection subject, ICollection expectation) { - return scope - .ForCondition(subject.Count > 0 || expectation.Count == 0) - .FailWith(", but found an empty collection.") - .Then - .ForCondition(subject.Count == 0 || expectation.Count > 0) - .FailWith($", but {{0}}{Environment.NewLine}contains {{1}} item(s).", - subject, - subject.Count); + return assertionChain + .WithExpectation("Expected {context:subject} to be a collection with {0} item(s){reason}", expectation.Count, + chain => chain + .ForCondition(subject.Count > 0 || expectation.Count == 0) + .FailWith(", but found an empty collection.") + .Then + .ForCondition(subject.Count == 0 || expectation.Count > 0) + .FailWith($", but {{0}}{Environment.NewLine}contains {{1}} item(s).", + subject, + subject.Count)); } - public static Continuation AssertCollectionHasEnoughItems(this IAssertionScope scope, ICollection subject, + public static Continuation AssertCollectionHasEnoughItems(this AssertionChain assertionChain, ICollection subject, ICollection expectation) { - return scope - .ForCondition(subject.Count >= expectation.Count) - .FailWith($", but {{0}}{Environment.NewLine}contains {{1}} item(s) less than{Environment.NewLine}{{2}}.", - subject, - expectation.Count - subject.Count, - expectation); + return assertionChain + .WithExpectation("Expected {context:subject} to be a collection with {0} item(s){reason}", expectation.Count, + chain => chain + .ForCondition(subject.Count >= expectation.Count) + .FailWith($", but {{0}}{Environment.NewLine}contains {{1}} item(s) less than{Environment.NewLine}{{2}}.", + subject, + expectation.Count - subject.Count, + expectation)); } - public static Continuation AssertCollectionHasNotTooManyItems(this IAssertionScope scope, ICollection subject, + public static Continuation AssertCollectionHasNotTooManyItems(this AssertionChain assertionChain, + ICollection subject, ICollection expectation) { - return scope - .ForCondition(subject.Count <= expectation.Count) - .FailWith($", but {{0}}{Environment.NewLine}contains {{1}} item(s) more than{Environment.NewLine}{{2}}.", - subject, - subject.Count - expectation.Count, - expectation); + return assertionChain + .WithExpectation("Expected {context:subject} to be a collection with {0} item(s){reason}", expectation.Count, + chain => chain + .ForCondition(subject.Count <= expectation.Count) + .FailWith($", but {{0}}{Environment.NewLine}contains {{1}} item(s) more than{Environment.NewLine}{{2}}.", + subject, + subject.Count - expectation.Count, + expectation)); } } diff --git a/Src/FluentAssertions/Equivalency/Steps/EqualityComparerEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/EqualityComparerEquivalencyStep.cs index 2ba66b8c9e..6bd954d16b 100644 --- a/Src/FluentAssertions/Equivalency/Steps/EqualityComparerEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/EqualityComparerEquivalencyStep.cs @@ -29,7 +29,8 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.ContinueWithNext; } - Execute.Assertion + AssertionChain.GetOrCreate() + .For(context) .BecauseOf(context.Reason.FormattedMessage, context.Reason.Arguments) .ForCondition(comparands.Subject is T) .FailWith("Expected {context:object} to be of type {0}{because}, but found {1}", typeof(T), comparands.Subject) diff --git a/Src/FluentAssertions/Equivalency/Steps/GenericDictionaryEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/GenericDictionaryEquivalencyStep.cs index f10a166a19..01b58bb808 100644 --- a/Src/FluentAssertions/Equivalency/Steps/GenericDictionaryEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/GenericDictionaryEquivalencyStep.cs @@ -11,7 +11,7 @@ public class GenericDictionaryEquivalencyStep : IEquivalencyStep { #pragma warning disable SA1110 // Allow opening parenthesis on new line to reduce line length private static readonly MethodInfo AssertDictionaryEquivalenceMethod = - new Action, IDictionary> (AssertDictionaryEquivalence).GetMethodInfo().GetGenericMethodDefinition(); #pragma warning restore SA1110 @@ -37,10 +37,13 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.ContinueWithNext; } - if (IsNotNull(comparands.Subject) - && EnsureSubjectIsOfTheExpectedDictionaryType(comparands, expectedDictionary) is { } actualDictionary) + var assertionChain = AssertionChain.GetOrCreate().For(context); + + if (IsNotNull(assertionChain, comparands.Subject) + && EnsureSubjectIsOfTheExpectedDictionaryType(assertionChain, comparands, expectedDictionary) is { } actualDictionary) { - AssertDictionaryEquivalence(comparands, context, valueChildNodes, actualDictionary, expectedDictionary); + AssertDictionaryEquivalence(comparands, assertionChain, context, valueChildNodes, actualDictionary, + expectedDictionary); } return EquivalencyResult.EquivalencyProven; @@ -57,14 +60,17 @@ private static bool IsNonGenericDictionary(object subject) @interface.IsGenericType && @interface.GetGenericTypeDefinition() == typeof(IDictionary<,>)); } - private static bool IsNotNull(object subject) + private static bool IsNotNull(AssertionChain assertionChain, object subject) { - return AssertionScope.Current + assertionChain .ForCondition(subject is not null) .FailWith("Expected {context:Subject} not to be {0}{reason}.", new object[] { null }); + + return assertionChain.Succeeded; } - private static DictionaryInterfaceInfo EnsureSubjectIsOfTheExpectedDictionaryType(Comparands comparands, + private static DictionaryInterfaceInfo EnsureSubjectIsOfTheExpectedDictionaryType(AssertionChain assertionChain, + Comparands comparands, DictionaryInterfaceInfo expectedDictionary) { var actualDictionary = DictionaryInterfaceInfo.FindFromWithKey(comparands.Subject.GetType(), "subject", @@ -78,7 +84,7 @@ private static DictionaryInterfaceInfo EnsureSubjectIsOfTheExpectedDictionaryTyp if (actualDictionary is null) { - AssertionScope.Current.FailWith( + assertionChain.FailWith( "Expected {context:subject} to be a dictionary or collection of key-value pairs that is keyed to " + $"type {expectedDictionary.Key}."); } @@ -87,7 +93,9 @@ private static DictionaryInterfaceInfo EnsureSubjectIsOfTheExpectedDictionaryTyp } private static void FailWithLengthDifference( - IDictionary subject, IDictionary expectation) + IDictionary subject, + IDictionary expectation, + AssertionChain assertionChain) // Type constraint of TExpectedKey is asymmetric in regards to TSubjectKey // but it is valid. This constraint is implicitly enforced by the dictionary interface info which is called before @@ -99,19 +107,18 @@ private static void FailWithLengthDifference 0; bool hasAdditionalKeys = keyDifference.AdditionalKeys.Count > 0; - Execute.Assertion - .WithExpectation("Expected {context:subject} to be a dictionary with {0} item(s){reason}, ", expectation.Count) - .ForCondition(!hasMissingKeys || hasAdditionalKeys) - .FailWith("but it misses key(s) {0}", keyDifference.MissingKeys) - .Then - .ForCondition(hasMissingKeys || !hasAdditionalKeys) - .FailWith("but has additional key(s) {0}", keyDifference.AdditionalKeys) - .Then - .ForCondition(!hasMissingKeys || !hasAdditionalKeys) - .FailWith("but it misses key(s) {0} and has additional key(s) {1}", keyDifference.MissingKeys, - keyDifference.AdditionalKeys) - .Then - .ClearExpectation(); + assertionChain + .WithExpectation("Expected {context:subject} to be a dictionary with {0} item(s){reason}, ", expectation.Count, + chain => chain + .ForCondition(!hasMissingKeys || hasAdditionalKeys) + .FailWith("but it misses key(s) {0}", keyDifference.MissingKeys) + .Then + .ForCondition(hasMissingKeys || !hasAdditionalKeys) + .FailWith("but has additional key(s) {0}", keyDifference.AdditionalKeys) + .Then + .ForCondition(!hasMissingKeys || !hasAdditionalKeys) + .FailWith("but it misses key(s) {0} and has additional key(s) {1}", keyDifference.MissingKeys, + keyDifference.AdditionalKeys)); } private static KeyDifference CalculateKeyDifference(missingKeys, additionalKeys); } - private static void AssertDictionaryEquivalence(Comparands comparands, IEquivalencyValidationContext context, - IValidateChildNodeEquivalency parent, DictionaryInterfaceInfo actualDictionary, DictionaryInterfaceInfo expectedDictionary) + private static void AssertDictionaryEquivalence(Comparands comparands, AssertionChain assertionChain, + IEquivalencyValidationContext context, + IValidateChildNodeEquivalency parent, DictionaryInterfaceInfo actualDictionary, + DictionaryInterfaceInfo expectedDictionary) { AssertDictionaryEquivalenceMethod .MakeGenericMethod(actualDictionary.Key, actualDictionary.Value, expectedDictionary.Key, expectedDictionary.Value) - .Invoke(null, [context, parent, context.Options, comparands.Subject, comparands.Expectation]); + .Invoke(null, [assertionChain, context, parent, context.Options, comparands.Subject, comparands.Expectation]); } private static void AssertDictionaryEquivalence( + AssertionChain assertionChain, EquivalencyValidationContext context, IValidateChildNodeEquivalency parent, IEquivalencyOptions options, @@ -165,7 +175,7 @@ private static void AssertDictionaryEquivalence(key)); + parent.AssertEquivalencyOf(nestedComparands, context.AsDictionaryItem(key)); } } else { + assertionChain.ReuseOnce(); subjectValue.Should().Be(expectation[key], context.Reason.FormattedMessage, context.Reason.Arguments); } } else { - AssertionScope.Current + assertionChain .BecauseOf(context.Reason) .FailWith("Expected {context:subject} to contain key {0}{reason}.", key); } diff --git a/Src/FluentAssertions/Equivalency/Steps/GenericEnumerableEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/GenericEnumerableEquivalencyStep.cs index 1ff5fb4148..f7e4e7297f 100644 --- a/Src/FluentAssertions/Equivalency/Steps/GenericEnumerableEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/GenericEnumerableEquivalencyStep.cs @@ -27,15 +27,17 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon Type[] interfaceTypes = GetIEnumerableInterfaces(expectedType); - AssertionScope.Current + var assertionChain = AssertionChain.GetOrCreate().For(context); + + assertionChain .ForCondition(interfaceTypes.Length == 1) .FailWith(() => new FailReason("{context:Expectation} implements {0}, so cannot determine which one " + "to use for asserting the equivalency of the collection. ", interfaceTypes.Select(type => "IEnumerable<" + type.GetGenericArguments().Single() + ">"))); - if (AssertSubjectIsCollection(comparands.Subject)) + if (AssertSubjectIsCollection(assertionChain, comparands.Subject)) { - var validator = new EnumerableEquivalencyValidator(valueChildNodes, context) + var validator = new EnumerableEquivalencyValidator(assertionChain, valueChildNodes, context) { Recursive = context.CurrentNode.IsRoot || context.Options.IsRecursive, OrderingRules = context.Options.OrderingRules @@ -62,20 +64,20 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon private static void HandleImpl(EnumerableEquivalencyValidator validator, object[] subject, IEnumerable expectation) => validator.Execute(subject, ToArray(expectation)); - private static bool AssertSubjectIsCollection(object subject) + private static bool AssertSubjectIsCollection(AssertionChain assertionChain, object subject) { - bool conditionMet = AssertionScope.Current + assertionChain .ForCondition(subject is not null) .FailWith("Expected {context:subject} not to be {0}.", new object[] { null }); - if (conditionMet) + if (assertionChain.Succeeded) { - conditionMet = AssertionScope.Current + assertionChain .ForCondition(IsCollection(subject.GetType())) .FailWith("Expected {context:subject} to be a collection, but it was a {0}", subject.GetType()); } - return conditionMet; + return assertionChain.Succeeded; } private static bool IsCollection(Type type) diff --git a/Src/FluentAssertions/Equivalency/Steps/SimpleEqualityEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/SimpleEqualityEquivalencyStep.cs index 0e2b955cf4..af3598ec1d 100644 --- a/Src/FluentAssertions/Equivalency/Steps/SimpleEqualityEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/SimpleEqualityEquivalencyStep.cs @@ -1,3 +1,5 @@ +using FluentAssertions.Execution; + namespace FluentAssertions.Equivalency.Steps; public class SimpleEqualityEquivalencyStep : IEquivalencyStep @@ -7,6 +9,10 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon { if (!context.Options.IsRecursive && !context.CurrentNode.IsRoot) { + AssertionChain.GetOrCreate() + .For(context) + .ReuseOnce(); + comparands.Subject.Should().Be(comparands.Expectation, context.Reason.FormattedMessage, context.Reason.Arguments); return EquivalencyResult.EquivalencyProven; diff --git a/Src/FluentAssertions/Equivalency/Steps/StringEqualityEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/StringEqualityEquivalencyStep.cs index 00a736626c..ec0e70fd24 100644 --- a/Src/FluentAssertions/Equivalency/Steps/StringEqualityEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/StringEqualityEquivalencyStep.cs @@ -15,21 +15,23 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.ContinueWithNext; } - if (!ValidateAgainstNulls(comparands, context.CurrentNode)) + var assertionChain = AssertionChain.GetOrCreate().For(context); + + if (!ValidateAgainstNulls(assertionChain, comparands, context.CurrentNode)) { return EquivalencyResult.EquivalencyProven; } - bool subjectIsString = ValidateSubjectIsString(comparands, context.CurrentNode); + bool subjectIsString = ValidateSubjectIsString(assertionChain, comparands, context.CurrentNode); if (subjectIsString) { string subject = (string)comparands.Subject; string expectation = (string)comparands.Expectation; + assertionChain.ReuseOnce(); subject.Should() - .Be(expectation, CreateOptions(context.Options), - context.Reason.FormattedMessage, context.Reason.Arguments); + .Be(expectation, CreateOptions(context.Options), context.Reason.FormattedMessage, context.Reason.Arguments); } return EquivalencyResult.EquivalencyProven; @@ -67,7 +69,7 @@ private static Func, EquivalencyOptions> return o; }; - private static bool ValidateAgainstNulls(Comparands comparands, INode currentNode) + private static bool ValidateAgainstNulls(AssertionChain assertionChain, Comparands comparands, INode currentNode) { object expected = comparands.Expectation; object subject = comparands.Subject; @@ -76,7 +78,7 @@ private static bool ValidateAgainstNulls(Comparands comparands, INode currentNod if (onlyOneNull) { - AssertionScope.Current.FailWith( + assertionChain.FailWith( $"Expected {currentNode.Description} to be {{0}}{{reason}}, but found {{1}}.", expected, subject); return false; @@ -85,16 +87,17 @@ private static bool ValidateAgainstNulls(Comparands comparands, INode currentNod return true; } - private static bool ValidateSubjectIsString(Comparands comparands, INode currentNode) + private static bool ValidateSubjectIsString(AssertionChain assertionChain, Comparands comparands, INode currentNode) { if (comparands.Subject is string) { return true; } - return - AssertionScope.Current - .FailWith($"Expected {currentNode} to be {{0}}, but found {{1}}.", - comparands.RuntimeType, comparands.Subject.GetType()); + assertionChain.FailWith( + $"Expected {currentNode} to be {{0}}, but found {{1}}.", + comparands.RuntimeType, comparands.Subject.GetType()); + + return assertionChain.Succeeded; } } diff --git a/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs index 23d022e952..0c43280b73 100644 --- a/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs @@ -15,9 +15,11 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return EquivalencyResult.ContinueWithNext; } + var assertionChain = AssertionChain.GetOrCreate().For(context); + if (comparands.Expectation is null) { - AssertionScope.Current + assertionChain .BecauseOf(context.Reason) .FailWith( "Expected {context:subject} to be {reason}, but found {0}.", @@ -25,7 +27,7 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon } else if (comparands.Subject is null) { - AssertionScope.Current + assertionChain .BecauseOf(context.Reason) .FailWith( "Expected {context:object} to be {0}{reason}, but found {1}.", @@ -55,7 +57,9 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon private static void AssertMemberEquality(Comparands comparands, IEquivalencyValidationContext context, IValidateChildNodeEquivalency parent, IMember selectedMember, IEquivalencyOptions options) { - IMember matchingMember = FindMatchFor(selectedMember, context.CurrentNode, comparands.Subject, options); + var assertionChain = AssertionChain.GetOrCreate().For(context); + + IMember matchingMember = FindMatchFor(selectedMember, context.CurrentNode, comparands.Subject, options, assertionChain); if (matchingMember is not null) { @@ -78,11 +82,11 @@ private static void AssertMemberEquality(Comparands comparands, IEquivalencyVali } private static IMember FindMatchFor(IMember selectedMember, INode currentNode, object subject, - IEquivalencyOptions config) + IEquivalencyOptions config, AssertionChain assertionChain) { IEnumerable query = from rule in config.MatchingRules - let match = rule.Match(selectedMember, subject, currentNode, config) + let match = rule.Match(selectedMember, subject, currentNode, config, assertionChain) where match is not null select match; diff --git a/Src/FluentAssertions/Equivalency/Steps/ValueTypeEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/ValueTypeEquivalencyStep.cs index c9270e421b..ee7d1b400d 100644 --- a/Src/FluentAssertions/Equivalency/Steps/ValueTypeEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/ValueTypeEquivalencyStep.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Steps; @@ -26,6 +27,10 @@ public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationCon return $"Treating {member.Description} as a value type because {strategyName}."; }); + AssertionChain.GetOrCreate() + .For(context) + .ReuseOnce(); + comparands.Subject.Should().Be(comparands.Expectation, context.Reason.FormattedMessage, context.Reason.Arguments); return EquivalencyResult.EquivalencyProven; diff --git a/Src/FluentAssertions/Equivalency/Steps/XAttributeEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/XAttributeEquivalencyStep.cs index 1f960da7b9..c403ed74d9 100644 --- a/Src/FluentAssertions/Equivalency/Steps/XAttributeEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/XAttributeEquivalencyStep.cs @@ -1,15 +1,19 @@ using System.Xml.Linq; +using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Steps; public class XAttributeEquivalencyStep : EquivalencyStep { - protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, - IValidateChildNodeEquivalency nested) + protected override EquivalencyResult OnHandle(Comparands comparands, + IEquivalencyValidationContext context, + IValidateChildNodeEquivalency nestedValidator) { var subject = (XAttribute)comparands.Subject; var expectation = (XAttribute)comparands.Expectation; + AssertionChain.GetOrCreate().For(context).ReuseOnce(); + subject.Should().Be(expectation, context.Reason.FormattedMessage, context.Reason.Arguments); return EquivalencyResult.EquivalencyProven; diff --git a/Src/FluentAssertions/Equivalency/Steps/XDocumentEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/XDocumentEquivalencyStep.cs index b7556b4788..b88921f45b 100644 --- a/Src/FluentAssertions/Equivalency/Steps/XDocumentEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/XDocumentEquivalencyStep.cs @@ -1,15 +1,19 @@ using System.Xml.Linq; +using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Steps; public class XDocumentEquivalencyStep : EquivalencyStep { - protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, - IValidateChildNodeEquivalency nested) + protected override EquivalencyResult OnHandle(Comparands comparands, + IEquivalencyValidationContext context, + IValidateChildNodeEquivalency nestedValidator) { var subject = (XDocument)comparands.Subject; var expectation = (XDocument)comparands.Expectation; + AssertionChain.GetOrCreate().For(context).ReuseOnce(); + subject.Should().BeEquivalentTo(expectation, context.Reason.FormattedMessage, context.Reason.Arguments); return EquivalencyResult.EquivalencyProven; diff --git a/Src/FluentAssertions/Equivalency/Steps/XElementEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/XElementEquivalencyStep.cs index 8e01b45938..9ed4e2d503 100644 --- a/Src/FluentAssertions/Equivalency/Steps/XElementEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/XElementEquivalencyStep.cs @@ -1,15 +1,19 @@ using System.Xml.Linq; +using FluentAssertions.Execution; namespace FluentAssertions.Equivalency.Steps; public class XElementEquivalencyStep : EquivalencyStep { - protected override EquivalencyResult OnHandle(Comparands comparands, IEquivalencyValidationContext context, - IValidateChildNodeEquivalency nested) + protected override EquivalencyResult OnHandle(Comparands comparands, + IEquivalencyValidationContext context, + IValidateChildNodeEquivalency nestedValidator) { var subject = (XElement)comparands.Subject; var expectation = (XElement)comparands.Expectation; + AssertionChain.GetOrCreate().For(context).ReuseOnce(); + subject.Should().BeEquivalentTo(expectation, context.Reason.FormattedMessage, context.Reason.Arguments); return EquivalencyResult.EquivalencyProven; diff --git a/Src/FluentAssertions/EventRaisingExtensions.cs b/Src/FluentAssertions/EventRaisingExtensions.cs index 95545d234b..15798cba20 100644 --- a/Src/FluentAssertions/EventRaisingExtensions.cs +++ b/Src/FluentAssertions/EventRaisingExtensions.cs @@ -24,15 +24,16 @@ public static IEventRecording WithSender(this IEventRecording eventRecording, ob { var eventsForSender = new List(); var otherSenders = new List(); + var assertion = AssertionChain.GetOrCreate(); foreach (OccurredEvent @event in eventRecording) { - bool hasSender = Execute.Assertion + assertion .ForCondition(@event.Parameters.Length > 0) .FailWith("Expected event from sender {0}, " + $"but event {eventRecording.EventName} does not have any parameters", expectedSender); - if (hasSender) + if (assertion.Succeeded) { object sender = @event.Parameters[0]; @@ -47,7 +48,7 @@ public static IEventRecording WithSender(this IEventRecording eventRecording, ob } } - Execute.Assertion + assertion .ForCondition(eventsForSender.Count > 0) .FailWith("Expected sender {0}, but found {1}.", () => expectedSender, @@ -84,7 +85,8 @@ public static IEventRecording WithArgs(this IEventRecording eventRecording, E bool foundMatchingEvent = eventsWithMatchingPredicate.Count > 0; - Execute.Assertion + AssertionChain + .GetOrCreate() .ForCondition(foundMatchingEvent) .FailWith("Expected at least one event with some argument of type <{0}> that matches {1}, but found none.", typeof(T), @@ -137,7 +139,8 @@ public static IEventRecording WithArgs(this IEventRecording eventRecording, p if (!foundMatchingEvent) { - Execute.Assertion + AssertionChain + .GetOrCreate() .FailWith( "Expected at least one event with some arguments of type <{0}> that pairwise match {1}, but found none.", typeof(T), diff --git a/Src/FluentAssertions/Events/EventAssertions.cs b/Src/FluentAssertions/Events/EventAssertions.cs index f7e861deca..6d624bda02 100644 --- a/Src/FluentAssertions/Events/EventAssertions.cs +++ b/Src/FluentAssertions/Events/EventAssertions.cs @@ -16,10 +16,12 @@ namespace FluentAssertions.Events; public class EventAssertions : ReferenceTypeAssertions> { private const string PropertyChangedEventName = nameof(INotifyPropertyChanged.PropertyChanged); + private readonly AssertionChain assertionChain; - protected internal EventAssertions(IMonitor monitor) - : base(monitor.Subject) + protected internal EventAssertions(IMonitor monitor, AssertionChain assertionChain) + : base(monitor.Subject, assertionChain) { + this.assertionChain = assertionChain; Monitor = monitor; } @@ -47,7 +49,7 @@ public IEventRecording Raise(string eventName, [StringSyntax("CompositeFormat")] if (!recording.Any()) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected object {0} to raise event {1}{reason}, but it did not.", Monitor.Subject, eventName); } @@ -74,7 +76,7 @@ public void NotRaise(string eventName, [StringSyntax("CompositeFormat")] string if (events.Any()) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected object {0} to not raise event {1}{reason}, but it did.", Monitor.Subject, eventName); } @@ -104,14 +106,14 @@ public IEventRecording RaisePropertyChangeFor(Expression> proper IEventRecording recording = Monitor.GetRecordingFor(PropertyChangedEventName); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(recording.Any()) .FailWith( "Expected object {0} to raise event {1} for property {2}{reason}, but it did not raise that event at all.", Monitor.Subject, PropertyChangedEventName, propertyName); - if (success) + if (assertionChain.Succeeded) { var actualPropertyNames = recording .SelectMany(@event => @event.Parameters.OfType()) @@ -119,7 +121,7 @@ public IEventRecording RaisePropertyChangeFor(Expression> proper .Distinct() .ToArray(); - Execute.Assertion + assertionChain .ForCondition(actualPropertyNames.Contains(propertyName)) .BecauseOf(because, becauseArgs) .FailWith("Expected object {0} to raise event {1} for property {2}{reason}, but it was only raised for {3}.", @@ -151,7 +153,7 @@ public void NotRaisePropertyChangeFor(Expression> propertyExpres if (propertyName is null) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!recording.Any()) .FailWith( @@ -160,7 +162,7 @@ public void NotRaisePropertyChangeFor(Expression> propertyExpres } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!recording.Any(@event => @event.IsAffectingPropertyName(propertyName))) .FailWith( diff --git a/Src/FluentAssertions/Events/EventMonitor.cs b/Src/FluentAssertions/Events/EventMonitor.cs index 26ebe0ad29..bcb2efbec6 100644 --- a/Src/FluentAssertions/Events/EventMonitor.cs +++ b/Src/FluentAssertions/Events/EventMonitor.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using FluentAssertions.Common; +using FluentAssertions.Execution; namespace FluentAssertions.Events; @@ -66,7 +67,7 @@ public void Clear() public EventAssertions Should() { - return new EventAssertions(this); + return new EventAssertions(this, AssertionChain.GetOrCreate()); } public IEventRecording GetRecordingFor(string eventName) diff --git a/Src/FluentAssertions/ExceptionAssertionsExtensions.cs b/Src/FluentAssertions/ExceptionAssertionsExtensions.cs index 900d1c9b4e..45de65a1a6 100644 --- a/Src/FluentAssertions/ExceptionAssertionsExtensions.cs +++ b/Src/FluentAssertions/ExceptionAssertionsExtensions.cs @@ -167,7 +167,8 @@ public static ExceptionAssertions WithParameterName( params object[] becauseArgs) where TException : ArgumentException { - Execute.Assertion + AssertionChain + .GetOrCreate() .ForCondition(parent.Which.ParamName == paramName) .BecauseOf(because, becauseArgs) .FailWith("Expected exception with parameter name {0}{reason}, but found {1}.", paramName, parent.Which.ParamName); diff --git a/Src/FluentAssertions/Execution/AssertionChain.cs b/Src/FluentAssertions/Execution/AssertionChain.cs new file mode 100644 index 0000000000..758560e303 --- /dev/null +++ b/Src/FluentAssertions/Execution/AssertionChain.cs @@ -0,0 +1,321 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Threading; +using FluentAssertions.Common; + +namespace FluentAssertions.Execution; + +public sealed class AssertionChain +{ + private readonly Func getCurrentScope; + private readonly ContextDataDictionary contextData = new(); + private string fallbackIdentifier = "object"; + private Func getCallerIdentifier; + private Func reason; + private bool? succeeded; + + // We need to keep track of this because we don't want the second successful assertion hide the first unsuccessful assertion + private Func expectation; + private string callerPostfix = string.Empty; + private string callerPrefix = string.Empty; + + private static readonly AsyncLocal Instance = new(); + + /// + /// Ensures that the next call to will reuse the current instance. + /// + public void ReuseOnce() + { + Instance.Value = this; + } + + public bool PreviousAssertionSucceeded { get; private set; } = true; + + public static AssertionChain GetOrCreate() + { + if (Instance.Value != null) + { + AssertionChain assertionChain = Instance.Value; + Instance.Value = null; + return assertionChain; + } + + return new AssertionChain(() => AssertionScope.Current, + () => FluentAssertions.CallerIdentifier.DetermineCallerIdentity()); + } + + private AssertionChain(Func getCurrentScope, Func getCallerIdentifier) + { + this.getCurrentScope = getCurrentScope; + + this.getCallerIdentifier = () => + { + var scopeName = getCurrentScope().Name(); + var callerIdentifier = getCallerIdentifier(); + + if (scopeName is null) + { + return callerIdentifier; + } + else if (callerIdentifier is null) + { + return scopeName; + } + else + { + return $"{scopeName}/{callerIdentifier}"; + } + }; + } + + public string CallerIdentifier => callerPrefix + getCallerIdentifier() + callerPostfix; + + /// + /// Adds an explanation of why the assertion is supposed to succeed to the scope. + /// + /// + /// An object containing a formatted phrase as is supported by explaining why the assertion + /// is needed, as well as zero or more objects to format the placeholders. + /// If the phrase does not start with the word because, it is prepended automatically.explaining why the assertion is needed. + /// + public AssertionChain BecauseOf(Reason reason) + { + return BecauseOf(reason.FormattedMessage, reason.Arguments); + } + + /// + /// Adds an explanation of why the assertion is supposed to succeed to the scope. + /// + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public AssertionChain BecauseOf(string because, params object[] becauseArgs) + { + reason = () => + { + try + { + string becauseOrEmpty = because ?? string.Empty; + + return becauseArgs?.Length > 0 + ? string.Format(CultureInfo.InvariantCulture, becauseOrEmpty, becauseArgs) + : becauseOrEmpty; + } + catch (FormatException formatException) + { + return + $"**WARNING** because message '{because}' could not be formatted with string.Format{Environment.NewLine}{formatException.StackTrace}"; + } + }; + + return this; + } + + public AssertionChain ForCondition(bool condition) + { + if (PreviousAssertionSucceeded) + { + succeeded = condition; + } + + return this; + } + + public AssertionChain ForConstraint(OccurrenceConstraint constraint, int actualOccurrences) + { + if (PreviousAssertionSucceeded) + { + constraint.RegisterContextData((key, value) => contextData.Add( + new ContextDataDictionary.DataItem(key, value, reportable: false, requiresFormatting: false))); + + succeeded = constraint.Assert(actualOccurrences); + } + + return this; + } + + public Continuation WithExpectation(string message, object arg1, Action chain) + { + return WithExpectation(message, chain, arg1); + } + + public Continuation WithExpectation(string message, object arg1, object arg2, Action chain) + { + return WithExpectation(message, chain, arg1, arg2); + } + + public Continuation WithExpectation(string message, Action chain) + { + return WithExpectation(message, chain, []); + } + + private Continuation WithExpectation(string message, Action chain, params object[] args) + { + if (PreviousAssertionSucceeded) + { + expectation = () => + { + var formatter = new FailureMessageFormatter(getCurrentScope().FormattingOptions) + .WithReason(reason?.Invoke() ?? string.Empty) + .WithContext(contextData) + .WithIdentifier(CallerIdentifier) + .WithFallbackIdentifier(fallbackIdentifier); + + return formatter.Format(message, args); + }; + + chain(this); + + expectation = null; + } + + return new Continuation(this); + } + + public AssertionChain WithDefaultIdentifier(string identifier) + { + fallbackIdentifier = identifier; + return this; + } + + public GivenSelector Given(Func selector) + { + return new GivenSelector(selector, this); + } + + internal Continuation FailWithPreFormatted(string formattedFailReason) + { + return FailWith(() => formattedFailReason); + } + + public Continuation FailWith(string message) + { + return FailWith(() => new FailReason(message)); + } + + public Continuation FailWith(string message, params object[] args) + { + return FailWith(() => new FailReason(message, args)); + } + + public Continuation FailWith(string message, params Func[] argProviders) + { + return FailWith( + () => new FailReason( + message, + argProviders.Select(a => a()).ToArray())); + } + + public Continuation FailWith(Func getFailureReason) + { + return FailWith(() => + { + var formatter = new FailureMessageFormatter(getCurrentScope().FormattingOptions) + .WithReason(reason?.Invoke() ?? string.Empty) + .WithContext(contextData) + .WithIdentifier(CallerIdentifier) + .WithFallbackIdentifier(fallbackIdentifier); + + FailReason failReason = getFailureReason(); + + return formatter.Format(failReason.Message, failReason.Args); + }); + } + + private Continuation FailWith(Func getFailureReason) + { + if (PreviousAssertionSucceeded) + { + PreviousAssertionSucceeded = succeeded is true; + + if (succeeded is not true) + { + string failure = getFailureReason(); + + if (expectation is not null) + { + failure = expectation() + failure; + } + + getCurrentScope().AddPreFormattedFailure(failure.Capitalize().RemoveTrailingWhitespaceFromLines()); + } + } + + // Reset the state for successive assertions on this object + succeeded = null; + + return new Continuation(this); + } + + public void OverrideCallerIdentifier(Func getCallerIdentifier) + { + this.getCallerIdentifier = getCallerIdentifier; + HasOverriddenCallerIdentifier = true; + } + + public AssertionChain WithCallerPostfix(string postfix) + { + callerPostfix = postfix; + HasOverriddenCallerIdentifier = true; + + return this; + } + + public AssertionChain WithCallerPrefix(string prefix) + { + callerPrefix = prefix; + HasOverriddenCallerIdentifier = true; + + return this; + } + + /// + /// Adds some information to the assertion that will be included in the message + /// that is emitted if an assertion fails. + /// + public void AddReportable(string key, string value) + { + getCurrentScope().AddReportable(key, value); + } + + /// + /// Adds some information to the assertion that will be included in the message + /// that is emitted if an assertion fails. The value is only calculated on failure. + /// + public void AddReportable(string key, Func getValue) + { + getCurrentScope().AddReportable(key, getValue); + } + + public AssertionChain WithReportable(string name, Func content) + { + getCurrentScope().AddReportable(name, content); + return this; + } + + public void AddPreFormattedFailure(string failure) + { + getCurrentScope().AddPreFormattedFailure(failure); + } + + /// + /// Gets a value indicating whether all assertions in the have succeeded. + /// + public bool Succeeded => PreviousAssertionSucceeded && succeeded is null or true; + + public AssertionChain UsingLineBreaks + { + get + { + getCurrentScope().FormattingOptions.UseLineBreaks = true; + return this; + } + } + + public bool HasOverriddenCallerIdentifier { get; private set; } +} diff --git a/Src/FluentAssertions/Execution/AssertionScope.cs b/Src/FluentAssertions/Execution/AssertionScope.cs index 2b70d559c7..a6705b60a6 100644 --- a/Src/FluentAssertions/Execution/AssertionScope.cs +++ b/Src/FluentAssertions/Execution/AssertionScope.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Linq; using System.Text; using System.Threading; @@ -14,60 +11,36 @@ namespace FluentAssertions.Execution; /// Represents an implicit or explicit scope within which multiple assertions can be collected. /// /// -/// This class is supposed to have a very short lifetime and is not safe to be used in assertions that cross thread-boundaries +/// This class is supposed to have a very short lifetime and is not safe to be used in assertion that cross thread-boundaries /// such as when using or . /// -public sealed class AssertionScope : IAssertionScope +// Remove all assertion logic from this class since it is superseded by the Assertion class +public sealed class AssertionScope : IDisposable { - #region Private Definitions - private readonly IAssertionStrategy assertionStrategy; - private readonly ContextDataDictionary contextData = new(); + private static readonly AsyncLocal CurrentScope = new(); + private readonly Func callerIdentityProvider = () => CallerIdentifier.DetermineCallerIdentity(); + private readonly ContextDataDictionary reportableData = new(); private readonly StringBuilder tracing = new(); - private Func reason; - - private static readonly AsyncLocal CurrentScope = new(); - private Func callerIdentityProvider = () => CallerIdentifier.DetermineCallerIdentity(); -#pragma warning disable CA2213 private AssertionScope parent; -#pragma warning restore CA2213 - private Func expectation; - private string fallbackIdentifier = "object"; - private bool? succeeded; - - private sealed class DeferredReportable - { - private readonly Lazy lazyValue; - - public DeferredReportable(Func valueFunc) - { - lazyValue = new Lazy(valueFunc); - } - - public override string ToString() => lazyValue.Value; - } - - #endregion /// /// Starts an unnamed scope within which multiple assertions can be executed /// and which will not throw until the scope is disposed. /// public AssertionScope() - : this(new CollectingAssertionStrategy(), context: null) + : this(() => null, new CollectingAssertionStrategy()) { - SetCurrentAssertionScope(this); } /// /// Starts a named scope within which multiple assertions can be executed /// and which will not throw until the scope is disposed. /// - public AssertionScope(string context) - : this(new CollectingAssertionStrategy(), new Lazy(() => context)) + public AssertionScope(string name) + : this(() => name, new CollectingAssertionStrategy()) { - SetCurrentAssertionScope(this); } /// @@ -76,19 +49,17 @@ public AssertionScope(string context) /// The assertion strategy for this scope. /// is . public AssertionScope(IAssertionStrategy assertionStrategy) - : this(assertionStrategy, context: null) + : this(() => null, assertionStrategy) { - SetCurrentAssertionScope(this); } /// /// Starts a named scope within which multiple assertions can be executed /// and which will not throw until the scope is disposed. /// - public AssertionScope(Lazy context) - : this(new CollectingAssertionStrategy(), context) + public AssertionScope(Func name) + : this(name, new CollectingAssertionStrategy()) { - SetCurrentAssertionScope(this); } /// @@ -96,47 +67,52 @@ public AssertionScope(Lazy context) /// /// The assertion strategy for this scope. /// is . - private AssertionScope(IAssertionStrategy assertionStrategy, Lazy context) + private AssertionScope(Func name, IAssertionStrategy assertionStrategy) { + parent = CurrentScope.Value; + CurrentScope.Value = this; + this.assertionStrategy = assertionStrategy ?? throw new ArgumentNullException(nameof(assertionStrategy)); - parent = GetCurrentAssertionScope(); - if (parent is not null) { - contextData.Add(parent.contextData); - reason = parent.reason; + // Combine the existing Name with the parent.Name if it exists. + Name = () => + { + var parentName = parent.Name(); + if (parentName.IsNullOrEmpty()) + { + return name(); + } + else if (name().IsNullOrEmpty()) + { + return parentName; + } + else + { + return parentName + "/" + name(); + } + }; + callerIdentityProvider = parent.callerIdentityProvider; FormattingOptions = parent.FormattingOptions.Clone(); - Context = JoinContexts(parent.Context, context); } else { - Context = context; + Name = name; } } - private static Lazy JoinContexts(Lazy outer, Lazy inner) - { - return (outer, inner) switch - { - (null, null) => null, - ({ } a, null) => a, - (null, { } b) => b, - ({ } a, { } b) => Join(a, b) - }; - - static Lazy Join(Lazy outer, Lazy inner) => - new(() => outer.Value + "/" + inner.Value); - } - /// - /// Gets or sets the context of the current assertion scope, e.g. the path of the object graph - /// that is being asserted on. The context is provided by a which - /// only gets evaluated when its value is actually needed (in most cases during a failure). + /// Gets or sets the name of the current assertion scope, e.g. the path of the object graph + /// that is being asserted on. /// - public Lazy Context { get; set; } + /// + /// The context is provided by a which + /// only gets evaluated when its value is actually needed (in most cases during a failure). + /// + public Func Name { get; } /// /// Gets the current thread-specific assertion scope. @@ -146,20 +122,10 @@ public static AssertionScope Current #pragma warning disable CA2000 // AssertionScope should not be disposed here get { - return GetCurrentAssertionScope() ?? new AssertionScope(new DefaultAssertionStrategy(), context: null); + return CurrentScope.Value ?? new AssertionScope(() => null, new DefaultAssertionStrategy()); } #pragma warning restore CA2000 - private set => SetCurrentAssertionScope(value); - } - - /// - public AssertionScope UsingLineBreaks - { - get - { - FormattingOptions.UseLineBreaks = true; - return this; - } + private set => CurrentScope.Value = value; } /// @@ -167,177 +133,6 @@ public AssertionScope UsingLineBreaks /// public FormattingOptions FormattingOptions { get; } = AssertionOptions.FormattingOptions.Clone(); - internal bool Succeeded => succeeded == true; - - /// - /// Adds an explanation of why the assertion is supposed to succeed to the scope. - /// - public AssertionScope BecauseOf(Reason reason) - { - return BecauseOf(reason.FormattedMessage, reason.Arguments); - } - - /// - public AssertionScope BecauseOf([StringSyntax("CompositeFormat")] string because, params object[] becauseArgs) - { - reason = () => - { - try - { - string becauseOrEmpty = because ?? string.Empty; - - return becauseArgs?.Length > 0 - ? string.Format(CultureInfo.InvariantCulture, becauseOrEmpty, becauseArgs) - : becauseOrEmpty; - } - catch (FormatException formatException) - { - return - $"**WARNING** because message '{because}' could not be formatted with string.Format{Environment.NewLine}{formatException.StackTrace}"; - } - }; - - return this; - } - - /// - public AssertionScope WithExpectation(string message, params object[] args) - { - Func localReason = reason; - - expectation = () => - { - var messageBuilder = new MessageBuilder(FormattingOptions); - string actualReason = localReason?.Invoke() ?? string.Empty; - string identifier = GetIdentifier(); - - return messageBuilder.Build(message, args, actualReason, contextData, identifier, fallbackIdentifier); - }; - - return this; - } - - internal void TrackComparands(object subject, object expectation) - { - contextData.Add(new ContextDataDictionary.DataItem("subject", subject, reportable: false, requiresFormatting: true)); - contextData.Add(new ContextDataDictionary.DataItem("expectation", expectation, reportable: false, requiresFormatting: true)); - } - - /// - public Continuation ClearExpectation() - { - expectation = null; - - // SMELL: Isn't this always going to return null? Or this method also called without FailWith (which sets the success state to null) - return new Continuation(this, Succeeded); - } - - public GivenSelector Given(Func selector) - { - return new GivenSelector(selector, this, continueAsserting: succeeded != false); - } - - /// - public AssertionScope ForCondition(bool condition) - { - succeeded = condition; - - return this; - } - - /// - public AssertionScope ForConstraint(OccurrenceConstraint constraint, int actualOccurrences) - { - constraint.RegisterReportables(this); - succeeded = constraint.Assert(actualOccurrences); - - return this; - } - - /// - public Continuation FailWith(Func failReasonFunc) - { - return FailWith(() => - { - string localReason = reason?.Invoke() ?? string.Empty; - var messageBuilder = new MessageBuilder(FormattingOptions); - string identifier = GetIdentifier(); - FailReason failReason = failReasonFunc(); - - string result = messageBuilder.Build(failReason.Message, failReason.Args, localReason, contextData, identifier, - fallbackIdentifier); - - return result; - }); - } - - internal Continuation FailWithPreFormatted(string formattedFailReason) => - FailWith(() => formattedFailReason); - - private Continuation FailWith(Func failReasonFunc) - { - try - { - bool failed = succeeded != true; - - if (failed) - { - string result = failReasonFunc(); - - if (expectation is not null) - { - result = expectation() + result; - } - - assertionStrategy.HandleFailure(result.Capitalize()); - - succeeded = false; - } - - return new Continuation(this, continueAsserting: !failed); - } - finally - { - succeeded = null; - } - } - - /// - public Continuation FailWith(string message) - { - return FailWith(() => new FailReason(message)); - } - - /// - public Continuation FailWith(string message, params object[] args) - { - return FailWith(() => new FailReason(message, args)); - } - - /// - public Continuation FailWith(string message, params Func[] argProviders) - { - return FailWith(() => new FailReason(message, - argProviders.Select(a => a()).ToArray())); - } - - private string GetIdentifier() - { - var identifier = Context?.Value; - - if (string.IsNullOrEmpty(identifier)) - { - identifier = CallerIdentity; - } - - return identifier; - } - - /// - /// Gets the identity of the caller associated with the current scope. - /// - public string CallerIdentity => callerIdentityProvider(); - /// /// Adds a pre-formatted failure message to the current scope. /// @@ -347,38 +142,30 @@ public void AddPreFormattedFailure(string formattedFailureMessage) } /// - /// Adds a block of tracing to the scope for reporting when an assertion fails. - /// - public void AppendTracing(string tracingBlock) - { - tracing.Append(tracingBlock); - } - - /// - /// Tracks a keyed object in the current scope that is excluded from the failure message in case an assertion fails. + /// Adds some information to the assertion scope that will be included in the message + /// that is emitted if an assertion fails. /// - public void AddNonReportable(string key, object value) + internal void AddReportable(string key, string value) { - contextData.Add(new ContextDataDictionary.DataItem(key, value, reportable: false, requiresFormatting: false)); + reportableData.Add(new ContextDataDictionary.DataItem(key, value, reportable: true, requiresFormatting: false)); } /// /// Adds some information to the assertion scope that will be included in the message - /// that is emitted if an assertion fails. + /// that is emitted if an assertion fails. The value is only calculated on failure. /// - public void AddReportable(string key, string value) + internal void AddReportable(string key, Func valueFunc) { - contextData.Add(new ContextDataDictionary.DataItem(key, value, reportable: true, requiresFormatting: false)); + reportableData.Add(new ContextDataDictionary.DataItem(key, new DeferredReportable(valueFunc), reportable: true, + requiresFormatting: false)); } /// - /// Adds some information to the assertion scope that will be included in the message - /// that is emitted if an assertion fails. The value is only calculated on failure. + /// Adds a block of tracing to the scope for reporting when an assertion fails. /// - public void AddReportable(string key, Func valueFunc) + public void AppendTracing(string tracingBlock) { - contextData.Add(new ContextDataDictionary.DataItem(key, new DeferredReportable(valueFunc), reportable: true, - requiresFormatting: false)); + tracing.Append(tracingBlock); } /// @@ -395,18 +182,10 @@ public bool HasFailures() return assertionStrategy.FailureMessages.Any(); } - /// - /// Gets data associated with the current scope and identified by . - /// - public T Get(string key) - { - return contextData.Get(key); - } - /// public void Dispose() { - SetCurrentAssertionScope(parent); + CurrentScope.Value = parent; if (parent is not null) { @@ -415,67 +194,26 @@ public void Dispose() parent.assertionStrategy.HandleFailure(failureMessage); } - parent.contextData.Add(contextData); + parent.reportableData.Add(reportableData); parent.AppendTracing(tracing.ToString()); parent = null; } else { - IDictionary reportable = contextData.GetReportable(); - if (tracing.Length > 0) { - reportable.Add("trace", tracing.ToString()); + reportableData.Add(new ContextDataDictionary.DataItem("trace", tracing.ToString(), reportable: true, requiresFormatting: false)); } - assertionStrategy.ThrowIfAny(reportable); + assertionStrategy.ThrowIfAny(reportableData.GetReportable()); } } - /// - public AssertionScope WithDefaultIdentifier(string identifier) + private sealed class DeferredReportable(Func valueFunc) { - fallbackIdentifier = identifier; - return this; - } - - /// - /// Allows the scope to assume that all assertions that happen within this scope are going to - /// be initiated by the same caller. - /// - public void AssumeSingleCaller() - { - // Since we know there's only one caller, we don't have to have every assertion determine the caller identity again - var provider = new Lazy(() => CallerIdentifier.DetermineCallerIdentity()); - callerIdentityProvider = () => provider.Value; - } - - private static AssertionScope GetCurrentAssertionScope() - { - return CurrentScope.Value; - } + private readonly Lazy lazyValue = new(valueFunc); - private static void SetCurrentAssertionScope(AssertionScope scope) - { - CurrentScope.Value = scope; + public override string ToString() => lazyValue.Value; } - - #region Explicit Implementation to support the interface - - IAssertionScope IAssertionScope.ForCondition(bool condition) => ForCondition(condition); - - IAssertionScope IAssertionScope.ForConstraint(OccurrenceConstraint constraint, int actualOccurrences) => - ForConstraint(constraint, actualOccurrences); - - IAssertionScope IAssertionScope.BecauseOf([StringSyntax("CompositeFormat")] string because, params object[] becauseArgs) => - BecauseOf(because, becauseArgs); - - IAssertionScope IAssertionScope.WithExpectation(string message, params object[] args) => WithExpectation(message, args); - - IAssertionScope IAssertionScope.WithDefaultIdentifier(string identifier) => WithDefaultIdentifier(identifier); - - IAssertionScope IAssertionScope.UsingLineBreaks => UsingLineBreaks; - - #endregion } diff --git a/Src/FluentAssertions/Execution/ContextDataDictionary.cs b/Src/FluentAssertions/Execution/ContextDataDictionary.cs index 8a079f6240..23ebd90548 100644 --- a/Src/FluentAssertions/Execution/ContextDataDictionary.cs +++ b/Src/FluentAssertions/Execution/ContextDataDictionary.cs @@ -44,7 +44,6 @@ public void Add(ContextDataDictionary contextDataDictionary) public void Add(DataItem item) { int existingItemIndex = items.FindIndex(i => i.Key == item.Key); - if (existingItemIndex >= 0) { items[existingItemIndex] = item; @@ -55,13 +54,7 @@ public void Add(DataItem item) } } - public T Get(string key) - { - DataItem item = items.SingleOrDefault(i => i.Key == key); - return (T)(item?.Value ?? default(T)); - } - - internal class DataItem(string key, object value, bool reportable, bool requiresFormatting) + public class DataItem(string key, object value, bool reportable, bool requiresFormatting) { public string Key { get; } = key; diff --git a/Src/FluentAssertions/Execution/Continuation.cs b/Src/FluentAssertions/Execution/Continuation.cs index b59ad7f4e7..b2f65e62a3 100644 --- a/Src/FluentAssertions/Execution/Continuation.cs +++ b/Src/FluentAssertions/Execution/Continuation.cs @@ -1,35 +1,17 @@ -namespace FluentAssertions.Execution; +namespace FluentAssertions.Execution; /// /// Enables chaining multiple assertions on an . /// public class Continuation { - private readonly AssertionScope sourceScope; - private readonly bool continueAsserting; - - internal Continuation(AssertionScope sourceScope, bool continueAsserting) + internal Continuation(AssertionChain parent) { - this.sourceScope = sourceScope; - this.continueAsserting = continueAsserting; + Then = parent; } /// /// Continues the assertion chain if the previous assertion was successful. /// - public IAssertionScope Then - { - get - { - return new ContinuedAssertionScope(sourceScope, continueAsserting); - } - } - - /// - /// Provides back-wards compatibility for code that expects to return a boolean. - /// - public static implicit operator bool(Continuation continuation) - { - return continuation.continueAsserting; - } + public AssertionChain Then { get; } } diff --git a/Src/FluentAssertions/Execution/ContinuationOfGiven.cs b/Src/FluentAssertions/Execution/ContinuationOfGiven.cs index 47ca54a889..ef42263f3f 100644 --- a/Src/FluentAssertions/Execution/ContinuationOfGiven.cs +++ b/Src/FluentAssertions/Execution/ContinuationOfGiven.cs @@ -1,15 +1,12 @@ -namespace FluentAssertions.Execution; +namespace FluentAssertions.Execution; /// -/// Enables chaining multiple assertions from a call. +/// Enables chaining multiple assertions from a call. /// public class ContinuationOfGiven { - private readonly bool succeeded; - - internal ContinuationOfGiven(GivenSelector parent, bool succeeded) + internal ContinuationOfGiven(GivenSelector parent) { - this.succeeded = succeeded; Then = parent; } @@ -18,11 +15,5 @@ internal ContinuationOfGiven(GivenSelector parent, bool succeeded) /// public GivenSelector Then { get; } - /// - /// Provides back-wards compatibility for code that expects to return a boolean. - /// - public static implicit operator bool(ContinuationOfGiven continuationOfGiven) - { - return continuationOfGiven.succeeded; - } + public bool Succeeded => Then.Succeeded; } diff --git a/Src/FluentAssertions/Execution/ContinuedAssertionScope.cs b/Src/FluentAssertions/Execution/ContinuedAssertionScope.cs deleted file mode 100644 index 1cc2d6dba9..0000000000 --- a/Src/FluentAssertions/Execution/ContinuedAssertionScope.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace FluentAssertions.Execution; - -/// -/// Allows chaining multiple assertion scopes together using . -/// -/// -/// If the parent scope has captured a failed assertion, -/// this class ensures that successive assertions are no longer evaluated. -/// -public sealed class ContinuedAssertionScope : IAssertionScope -{ - private readonly AssertionScope predecessor; - private readonly bool continueAsserting; - - internal ContinuedAssertionScope(AssertionScope predecessor, bool continueAsserting) - { - this.predecessor = predecessor; - this.continueAsserting = continueAsserting; - } - - /// - public GivenSelector Given(Func selector) - { - if (continueAsserting) - { - return predecessor.Given(selector); - } - - return new GivenSelector(() => default, predecessor, continueAsserting: false); - } - - /// - public IAssertionScope ForCondition(bool condition) - { - if (continueAsserting) - { - return predecessor.ForCondition(condition); - } - - return this; - } - - /// - public IAssertionScope ForConstraint(OccurrenceConstraint constraint, int actualOccurrences) - { - if (continueAsserting) - { - return predecessor.ForConstraint(constraint, actualOccurrences); - } - - return this; - } - - /// - public Continuation FailWith(string message) - { - if (continueAsserting) - { - return predecessor.FailWith(message); - } - - return new Continuation(predecessor, continueAsserting: false); - } - - /// - public Continuation FailWith(string message, params Func[] argProviders) - { - if (continueAsserting) - { - return predecessor.FailWith(message, argProviders); - } - - return new Continuation(predecessor, continueAsserting: false); - } - - /// - public Continuation FailWith(Func failReasonFunc) - { - if (continueAsserting) - { - return predecessor.FailWith(failReasonFunc); - } - - return new Continuation(predecessor, continueAsserting: false); - } - - /// - public Continuation FailWith(string message, params object[] args) - { - if (continueAsserting) - { - return predecessor.FailWith(message, args); - } - - return new Continuation(predecessor, continueAsserting: false); - } - - /// - public IAssertionScope BecauseOf([StringSyntax("CompositeFormat")] string because, params object[] becauseArgs) - { - if (continueAsserting) - { - return predecessor.BecauseOf(because, becauseArgs); - } - - return this; - } - - /// - public Continuation ClearExpectation() - { - predecessor.ClearExpectation(); - - return new Continuation(predecessor, continueAsserting); - } - - /// - public IAssertionScope WithExpectation(string message, params object[] args) - { - if (continueAsserting) - { - return predecessor.WithExpectation(message, args); - } - - return this; - } - - /// - public IAssertionScope WithDefaultIdentifier(string identifier) - { - if (continueAsserting) - { - return predecessor.WithDefaultIdentifier(identifier); - } - - return this; - } - - /// - public IAssertionScope UsingLineBreaks => predecessor.UsingLineBreaks; - - /// - public string[] Discard() - { - return predecessor.Discard(); - } - - /// - public void Dispose() - { - predecessor.Dispose(); - } -} diff --git a/Src/FluentAssertions/Execution/Execute.cs b/Src/FluentAssertions/Execution/Execute.cs deleted file mode 100644 index 9533780cb4..0000000000 --- a/Src/FluentAssertions/Execution/Execute.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FluentAssertions.Common; - -namespace FluentAssertions.Execution; - -/// -/// Helper class for verifying a condition and/or throwing a test harness specific exception representing an assertion failure. -/// -public static class Execute -{ - /// - /// Gets an object that wraps and executes a conditional or unconditional assertion. - /// - public static AssertionScope Assertion - { - get - { - Services.EnsureInitialized(); - return AssertionScope.Current; - } - } -} diff --git a/Src/FluentAssertions/Execution/FailReason.cs b/Src/FluentAssertions/Execution/FailReason.cs index 420f3a2e47..27bab552db 100644 --- a/Src/FluentAssertions/Execution/FailReason.cs +++ b/Src/FluentAssertions/Execution/FailReason.cs @@ -1,15 +1,15 @@ namespace FluentAssertions.Execution; /// -/// Represents assertion fail reason. Contains the message and arguments for message's numbered placeholders. +/// Represents the assertion fail reason. Contains the message and arguments for message's numbered placeholders. /// /// /// In addition to the numbered -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 . +/// assertion as passed to . /// /// Other named placeholders will be replaced with the scope data passed through -/// and . +/// . /// /// /// Finally, a description of the current subject can be passed through the {context:description} placeholder. diff --git a/Src/FluentAssertions/Execution/MessageBuilder.cs b/Src/FluentAssertions/Execution/FailureMessageFormatter.cs similarity index 68% rename from Src/FluentAssertions/Execution/MessageBuilder.cs rename to Src/FluentAssertions/Execution/FailureMessageFormatter.cs index 65460111f5..3559ea97b6 100644 --- a/Src/FluentAssertions/Execution/MessageBuilder.cs +++ b/Src/FluentAssertions/Execution/FailureMessageFormatter.cs @@ -1,39 +1,87 @@ -#region - -using System; +using System; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using FluentAssertions.Common; using FluentAssertions.Formatting; -#endregion - namespace FluentAssertions.Execution; /// /// Encapsulates expanding the various placeholders supported in a failure message. /// -internal class MessageBuilder +internal class FailureMessageFormatter(FormattingOptions formattingOptions) { - private readonly FormattingOptions formattingOptions; + private static readonly char[] Blanks = ['\r', '\n', ' ', '\t']; + private string reason; + private ContextDataDictionary contextData; + private string identifier; + private string fallbackIdentifier; + + public FailureMessageFormatter WithReason(string reason) + { + this.reason = SanitizeReason(reason ?? string.Empty); + return this; + } + + private static string SanitizeReason(string reason) + { + if (!string.IsNullOrEmpty(reason)) + { + reason = EnsurePrefix("because", reason); + reason = reason.EscapePlaceholders(); + + return StartsWithBlank(reason) ? reason : " " + reason; + } - #region Private Definitions + return string.Empty; + } + + // SMELL: looks way too complex just to retain the leading whitespace + private static string EnsurePrefix(string prefix, string text) + { + string leadingBlanks = ExtractLeadingBlanksFrom(text); + string textWithoutLeadingBlanks = text.Substring(leadingBlanks.Length); + + return !textWithoutLeadingBlanks.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) + ? leadingBlanks + prefix + " " + textWithoutLeadingBlanks + : text; + } + + private static string ExtractLeadingBlanksFrom(string text) + { + string trimmedText = text.TrimStart(Blanks); + int leadingBlanksCount = text.Length - trimmedText.Length; + + return text.Substring(0, leadingBlanksCount); + } + + private static bool StartsWithBlank(string text) + { + return text.Length > 0 && Blanks.Contains(text[0]); + } - private readonly char[] blanks = ['\r', '\n', ' ', '\t']; + public FailureMessageFormatter WithContext(ContextDataDictionary contextData) + { + this.contextData = contextData; + return this; + } - #endregion + public FailureMessageFormatter WithIdentifier(string identifier) + { + this.identifier = identifier; + return this; + } - public MessageBuilder(FormattingOptions formattingOptions) + public FailureMessageFormatter WithFallbackIdentifier(string fallbackIdentifier) { - this.formattingOptions = formattingOptions; + this.fallbackIdentifier = fallbackIdentifier; + return this; } - // SMELL: Too many parameters. - public string Build(string message, object[] messageArgs, string reason, ContextDataDictionary contextData, string identifier, - string fallbackIdentifier) + public string Format(string message, object[] messageArgs) { - message = message.Replace("{reason}", SanitizeReason(reason), StringComparison.Ordinal); + message = message.Replace("{reason}", reason, StringComparison.Ordinal); message = SubstituteIdentifier(message, identifier?.EscapePlaceholders(), fallbackIdentifier); @@ -91,7 +139,7 @@ private static string SubstituteContextualTags(string message, ContextDataDictio private string FormatArgumentPlaceholders(string failureMessage, object[] failureArgs) { - string[] values = failureArgs.Select(a => Formatter.ToString(a, formattingOptions)).ToArray(); + object[] values = failureArgs.Select(object (a) => Formatter.ToString(a, formattingOptions)).ToArray(); try { @@ -103,41 +151,4 @@ private string FormatArgumentPlaceholders(string failureMessage, object[] failur $"**WARNING** failure message '{failureMessage}' could not be formatted with string.Format{Environment.NewLine}{formatException.StackTrace}"; } } - - private string SanitizeReason(string reason) - { - if (!string.IsNullOrEmpty(reason)) - { - reason = EnsurePrefix("because", reason); - reason = reason.EscapePlaceholders(); - - return StartsWithBlank(reason) ? reason : " " + reason; - } - - return string.Empty; - } - - // SMELL: looks way too complex just to retain the leading whitespace - private string EnsurePrefix(string prefix, string text) - { - string leadingBlanks = ExtractLeadingBlanksFrom(text); - string textWithoutLeadingBlanks = text.Substring(leadingBlanks.Length); - - return !textWithoutLeadingBlanks.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) - ? leadingBlanks + prefix + " " + textWithoutLeadingBlanks - : text; - } - - private string ExtractLeadingBlanksFrom(string text) - { - string trimmedText = text.TrimStart(blanks); - int leadingBlanksCount = text.Length - trimmedText.Length; - - return text.Substring(0, leadingBlanksCount); - } - - private bool StartsWithBlank(string text) - { - return text.Length > 0 && blanks.Contains(text[0]); - } } diff --git a/Src/FluentAssertions/Execution/GivenSelector.cs b/Src/FluentAssertions/Execution/GivenSelector.cs index b2d776fb26..0c2e42798f 100644 --- a/Src/FluentAssertions/Execution/GivenSelector.cs +++ b/Src/FluentAssertions/Execution/GivenSelector.cs @@ -1,28 +1,27 @@ -using System; +using System; using System.Linq; using FluentAssertions.Common; namespace FluentAssertions.Execution; /// -/// Represents a chaining object returned from to continue the assertion using +/// Represents a chaining object returned from to continue the assertion using /// an object returned by a selector. /// public class GivenSelector { - private readonly AssertionScope predecessor; - private readonly T subject; + private readonly AssertionChain assertionChain; + private readonly T selector; - private bool continueAsserting; - - internal GivenSelector(Func selector, AssertionScope predecessor, bool continueAsserting) + internal GivenSelector(Func selector, AssertionChain assertionChain) { - this.predecessor = predecessor; - this.continueAsserting = continueAsserting; + this.assertionChain = assertionChain; - subject = continueAsserting ? selector() : default; + this.selector = assertionChain.Succeeded ? selector() : default; } + public bool Succeeded => assertionChain.Succeeded; + /// /// Specify the condition that must be satisfied upon the subject selected through a prior selector. /// @@ -31,78 +30,47 @@ internal GivenSelector(Func selector, AssertionScope predecessor, bool contin /// /// /// The condition will not be evaluated if the prior assertion failed, - /// nor will throw any exceptions. + /// nor will throw any exceptions. /// /// is . public GivenSelector ForCondition(Func predicate) { Guard.ThrowIfArgumentIsNull(predicate); - if (continueAsserting) + if (assertionChain.Succeeded) { - predecessor.ForCondition(predicate(subject)); + assertionChain.ForCondition(predicate(selector)); } return this; } - /// - /// The will not be invoked if the prior assertion failed, - /// nor will throw any exceptions. - /// - /// - /// is . public GivenSelector Given(Func selector) { Guard.ThrowIfArgumentIsNull(selector); - return new GivenSelector(() => selector(subject), predecessor, continueAsserting); + return new GivenSelector(() => selector(this.selector), assertionChain); } - /// public ContinuationOfGiven FailWith(string message) { return FailWith(message, Array.Empty()); } - /// - /// - /// The will not be invoked if the prior assertion failed, - /// nor will throw any exceptions. - /// - /// public ContinuationOfGiven FailWith(string message, params Func[] args) { - if (continueAsserting) + if (assertionChain.PreviousAssertionSucceeded) { - object[] mappedArguments = args.Select(a => a(subject)).ToArray(); + object[] mappedArguments = args.Select(a => a(selector)).ToArray(); return FailWith(message, mappedArguments); } - return new ContinuationOfGiven(this, succeeded: false); + return new ContinuationOfGiven(this); } - /// - /// - /// The will not be invoked if the prior assertion failed, - /// nor will throw any exceptions. - /// - /// public ContinuationOfGiven FailWith(string message, params object[] args) { - if (continueAsserting) - { - continueAsserting = predecessor.FailWith(message, args); - return new ContinuationOfGiven(this, continueAsserting); - } - - return new ContinuationOfGiven(this, succeeded: false); - } - - /// - public ContinuationOfGiven ClearExpectation() - { - predecessor.ClearExpectation(); - return new ContinuationOfGiven(this, continueAsserting); + assertionChain.FailWith(message, args); + return new ContinuationOfGiven(this); } } diff --git a/Src/FluentAssertions/Execution/IAssertionScope.cs b/Src/FluentAssertions/Execution/IAssertionScope.cs deleted file mode 100644 index d114564ac5..0000000000 --- a/Src/FluentAssertions/Execution/IAssertionScope.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace FluentAssertions.Execution; - -public interface IAssertionScope : IDisposable -{ - /// - /// Allows to safely select the subject for successive assertions. - /// - /// - /// Selector which result is passed to successive calls to . - /// - GivenSelector Given(Func selector); - - /// - /// Specify the condition that must be satisfied. - /// - /// - /// If the assertion will be treated as successful and no exceptions will be thrown. - /// - IAssertionScope ForCondition(bool condition); - - /// - /// Makes assertion fail when does not match . - /// - /// The occurrence description in natural language could then be inserted in failure message by using - /// {expectedOccurrence} placeholder in message parameters of and its - /// overloaded versions. - /// - /// - /// defining the number of expected occurrences. - /// The number of actual occurrences. - IAssertionScope ForConstraint(OccurrenceConstraint constraint, int actualOccurrences); - - /// - /// Sets the failure message when the assertion is not met, or completes the failure message set to a prior call to - /// . - /// - /// - /// Messages may contain a few specialized placeholders. For instance, {reason} will be replaced with the reason - /// of the assertion as passed to . - /// - /// Other named placeholders will be replaced with the scope data passed through - /// and . - /// - /// - /// 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 constructor. - /// - /// - /// If an expectation was set through a prior call to , then the failure - /// message is appended to that expectation. - /// - /// - /// The format string that represents the failure message. - Continuation FailWith(string message); - - /// - /// Sets the failure message when the assertion is not met, or completes the failure message set to a prior call to - /// . - /// will not be called unless the assertion is not met. - /// - /// Function returning object on demand. Called only when the assertion is not met. - Continuation FailWith(Func failReasonFunc); - - /// - /// Sets the failure message when the assertion is not met, or completes the failure message set to a prior call to - /// . - /// - /// - /// In addition to the numbered -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 . - /// - /// Other named placeholders will be replaced with the scope data passed through - /// and . - /// - /// - /// 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 constructor. - /// - /// - /// Note that only 10 are supported in combination with a {reason}. - /// - /// - /// If an expectation was set through a prior call to , then the failure - /// message is appended to that expectation. - /// - /// - /// The format string that represents the failure message. - /// Optional arguments to any numbered placeholders. - Continuation FailWith(string message, params object[] args); - - /// - /// Sets the failure message when the assertion is not met, or completes the failure message set to a prior call to - /// , - /// but postpones evaluation of the formatting arguments until the assertion really fails. - /// - /// - /// In addition to the numbered -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 . - /// - /// Other named placeholders will be replaced with the scope data passed through - /// and . - /// - /// - /// 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 constructor. - /// - /// - /// Note that only 10 are supported in combination with a {reason}. - /// - /// - /// If an expectation was set through a prior call to , then the failure - /// message is appended to that expectation. - /// - /// - /// The format string that represents the failure message. - /// Optional lazily evaluated arguments to any numbered placeholders - public Continuation FailWith(string message, params Func[] argProviders); - - /// - /// Specify the reason why you expect the condition to be . - /// - /// - /// A formatted phrase compatible with explaining why the condition should - /// be satisfied. If the phrase does not start with the word because, it is prepended to the message. - /// - /// If the format of or is not compatible with - /// , then a warning message is returned instead. - /// - /// - /// - /// Zero or more values to use for filling in any compatible placeholders. - /// - IAssertionScope BecauseOf([StringSyntax("CompositeFormat")] string because, params object[] becauseArgs); - - /// - /// Clears the expectation set by . - /// - // SMELL: It would be better to give the expectation an explicit scope, but that would be a breaking change. - Continuation ClearExpectation(); - - /// - /// Sets the expectation part of the failure message when the assertion is not met. - /// - /// - /// In addition to the numbered -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 . - /// - /// Other named placeholders will be replaced with the scope data passed through - /// and . - /// - /// - /// 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 constructor. - /// - /// - /// Note that only 10 are supported in combination with a {reason}. - /// - /// - /// The format string that represents the failure message. - /// Optional arguments to any numbered placeholders. - IAssertionScope WithExpectation(string message, params object[] args); - - /// - /// Defines the name of the subject in case this cannot be extracted from the source code. - /// - IAssertionScope WithDefaultIdentifier(string identifier); - - /// - /// Forces the formatters, that support it, to add the necessary line breaks. - /// - /// - /// This is just shorthand for modifying the property. - /// - IAssertionScope UsingLineBreaks { get; } - - /// - /// Discards and returns the failures that happened up to now. - /// - string[] Discard(); -} diff --git a/Src/FluentAssertions/Formatting/FormattedObjectGraph.cs b/Src/FluentAssertions/Formatting/FormattedObjectGraph.cs index 3f5ba3550e..e86f2185b4 100644 --- a/Src/FluentAssertions/Formatting/FormattedObjectGraph.cs +++ b/Src/FluentAssertions/Formatting/FormattedObjectGraph.cs @@ -15,13 +15,19 @@ namespace FluentAssertions.Formatting; /// to the maximum number of lines provided through its constructor. It will throw /// a if the number of lines exceeds the maximum. /// -public class FormattedObjectGraph(int maxLines) +public class FormattedObjectGraph { + private readonly int maxLines; private readonly List lines = []; private readonly StringBuilder lineBuilder = new(); private int indentation; private string lineBuilderWhitespace = string.Empty; + public FormattedObjectGraph(int maxLines) + { + this.maxLines = maxLines; + } + /// /// The number of spaces that should be used by every indentation level. /// @@ -93,13 +99,14 @@ private void InsertInitialNewLine() private void FlushCurrentLine() { - if (lineBuilder.Length > 0) + string line = lineBuilder.ToString().TrimEnd(); + if (line.Length > 0) { - AppendWithoutExceedingMaximumLines($"{lineBuilderWhitespace}{lineBuilder}"); - - lineBuilder.Clear(); - lineBuilderWhitespace = Whitespace; + AppendWithoutExceedingMaximumLines(lineBuilderWhitespace + line); } + + lineBuilder.Clear(); + lineBuilderWhitespace = Whitespace; } private void AppendWithoutExceedingMaximumLines(string line) diff --git a/Src/FluentAssertions/Formatting/Formatter.cs b/Src/FluentAssertions/Formatting/Formatter.cs index d61ea84f9f..4ac3f13bfd 100644 --- a/Src/FluentAssertions/Formatting/Formatter.cs +++ b/Src/FluentAssertions/Formatting/Formatter.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using FluentAssertions.Common; -using FluentAssertions.Equivalency; using FluentAssertions.Equivalency.Execution; using FluentAssertions.Execution; using FluentAssertions.Xml; @@ -27,6 +26,7 @@ public static class Formatter new XElementValueFormatter(), new XAttributeValueFormatter(), new PropertyInfoFormatter(), + new MethodInfoFormatter(), new NullValueFormatter(), new GuidValueFormatter(), new DateTimeOffsetValueFormatter(), @@ -214,7 +214,7 @@ public bool TryPush(string path, object value) string fullPath = GetFullPath(); var reference = new ObjectReference(value, fullPath); - return !tracker.IsCyclicReference(reference, CyclicReferenceHandling.Ignore); + return !tracker.IsCyclicReference(reference); } private string GetFullPath() => string.Join(".", pathStack.Reverse()); diff --git a/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs b/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs index c9746c2901..4ec7de86e0 100644 --- a/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs +++ b/Src/FluentAssertions/Formatting/PropertyInfoFormatter.cs @@ -18,6 +18,13 @@ public bool CanHandle(object value) public void Format(object value, FormattedObjectGraph formattedGraph, FormattingContext context, FormatChild formatChild) { - formattedGraph.AddFragment(((PropertyInfo)value).Name); + if (value is not PropertyInfo property) + { + formattedGraph.AddFragment(""); + } + else + { + formattedGraph.AddFragment($"{property.DeclaringType?.Name ?? string.Empty}.{property.Name}"); + } } } diff --git a/Src/FluentAssertions/Numeric/ByteAssertions.cs b/Src/FluentAssertions/Numeric/ByteAssertions.cs index 6467e86f5d..ccd7399bcf 100644 --- a/Src/FluentAssertions/Numeric/ByteAssertions.cs +++ b/Src/FluentAssertions/Numeric/ByteAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class ByteAssertions : NumericAssertions { - internal ByteAssertions(byte value) - : base(value) + internal ByteAssertions(byte value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/ComparableTypeAssertions.cs b/Src/FluentAssertions/Numeric/ComparableTypeAssertions.cs index 805812df65..3a40a75be1 100644 --- a/Src/FluentAssertions/Numeric/ComparableTypeAssertions.cs +++ b/Src/FluentAssertions/Numeric/ComparableTypeAssertions.cs @@ -16,8 +16,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] public class ComparableTypeAssertions : ComparableTypeAssertions> { - public ComparableTypeAssertions(IComparable value) - : base(value) + public ComparableTypeAssertions(IComparable value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -30,10 +30,12 @@ public class ComparableTypeAssertions : ReferenceTypeAssertions< where TAssertions : ComparableTypeAssertions { private const int Equal = 0; + private readonly AssertionChain assertionChain; - public ComparableTypeAssertions(IComparable value) - : base(value) + public ComparableTypeAssertions(IComparable value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -51,7 +53,7 @@ public ComparableTypeAssertions(IComparable value) /// public AndConstraint Be(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Equals(Subject, expected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be equal to {0}{reason}, but found {1}.", expected, Subject); @@ -112,7 +114,7 @@ public AndConstraint BeEquivalentTo(TExpectation expe EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); var context = new EquivalencyValidationContext( - Node.From(() => AssertionScope.Current.CallerIdentity), options) + Node.From(() => CurrentAssertionChain.CallerIdentifier), options) { Reason = new Reason(because, becauseArgs), TraceWriter = options.TraceWriter @@ -145,7 +147,7 @@ public AndConstraint BeEquivalentTo(TExpectation expe /// public AndConstraint NotBe(T unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Equals(Subject, unexpected)) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:object} to be equal to {0}{reason}.", unexpected); @@ -169,7 +171,7 @@ public AndConstraint NotBe(T unexpected, [StringSyntax("CompositeFo /// public AndConstraint BeRankedEquallyTo(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.CompareTo(expected) == Equal) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} {0} to be ranked as equal to {1}{reason}.", Subject, expected); @@ -193,7 +195,7 @@ public AndConstraint BeRankedEquallyTo(T expected, [StringSyntax("C /// public AndConstraint NotBeRankedEquallyTo(T unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.CompareTo(unexpected) != Equal) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} {0} not to be ranked as equal to {1}{reason}.", Subject, unexpected); @@ -216,7 +218,7 @@ public AndConstraint NotBeRankedEquallyTo(T unexpected, [StringSynt /// public AndConstraint BeLessThan(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.CompareTo(expected) < Equal) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} {0} to be less than {1}{reason}.", Subject, expected); @@ -239,7 +241,7 @@ public AndConstraint BeLessThan(T expected, [StringSyntax("Composit /// public AndConstraint BeLessThanOrEqualTo(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.CompareTo(expected) <= Equal) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} {0} to be less than or equal to {1}{reason}.", Subject, expected); @@ -262,7 +264,7 @@ public AndConstraint BeLessThanOrEqualTo(T expected, [StringSyntax( /// public AndConstraint BeGreaterThan(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.CompareTo(expected) > Equal) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} {0} to be greater than {1}{reason}.", Subject, expected); @@ -285,7 +287,7 @@ public AndConstraint BeGreaterThan(T expected, [StringSyntax("Compo /// public AndConstraint BeGreaterThanOrEqualTo(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.CompareTo(expected) >= Equal) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} {0} to be greater than or equal to {1}{reason}.", Subject, expected); @@ -315,7 +317,7 @@ public AndConstraint BeGreaterThanOrEqualTo(T expected, [StringSynt public AndConstraint BeInRange(T minimumValue, T maximumValue, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.CompareTo(minimumValue) >= Equal && Subject.CompareTo(maximumValue) <= Equal) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be between {0} and {1}{reason}, but found {2}.", @@ -346,7 +348,7 @@ public AndConstraint BeInRange(T minimumValue, T maximumValue, public AndConstraint NotBeInRange(T minimumValue, T maximumValue, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!(Subject.CompareTo(minimumValue) >= Equal && Subject.CompareTo(maximumValue) <= Equal)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to not be between {0} and {1}{reason}, but found {2}.", @@ -382,7 +384,7 @@ public AndConstraint BeOneOf(params T[] validValues) public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(validValues.Any(val => Equals(Subject, val))) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be one of {0}{reason}, but found {1}.", validValues, Subject); diff --git a/Src/FluentAssertions/Numeric/DecimalAssertions.cs b/Src/FluentAssertions/Numeric/DecimalAssertions.cs index 0b0d91bf00..aa31679f46 100644 --- a/Src/FluentAssertions/Numeric/DecimalAssertions.cs +++ b/Src/FluentAssertions/Numeric/DecimalAssertions.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -10,8 +11,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class DecimalAssertions : NumericAssertions { - internal DecimalAssertions(decimal value) - : base(value) + internal DecimalAssertions(decimal value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/DoubleAssertions.cs b/Src/FluentAssertions/Numeric/DoubleAssertions.cs index d8395924b5..0596191408 100644 --- a/Src/FluentAssertions/Numeric/DoubleAssertions.cs +++ b/Src/FluentAssertions/Numeric/DoubleAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class DoubleAssertions : NumericAssertions { - internal DoubleAssertions(double value) - : base(value) + internal DoubleAssertions(double value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/Int16Assertions.cs b/Src/FluentAssertions/Numeric/Int16Assertions.cs index d4391d4115..7d7b9c2102 100644 --- a/Src/FluentAssertions/Numeric/Int16Assertions.cs +++ b/Src/FluentAssertions/Numeric/Int16Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class Int16Assertions : NumericAssertions { - internal Int16Assertions(short value) - : base(value) + internal Int16Assertions(short value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/Int32Assertions.cs b/Src/FluentAssertions/Numeric/Int32Assertions.cs index 44baf64bd6..cfcb25f472 100644 --- a/Src/FluentAssertions/Numeric/Int32Assertions.cs +++ b/Src/FluentAssertions/Numeric/Int32Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class Int32Assertions : NumericAssertions { - internal Int32Assertions(int value) - : base(value) + internal Int32Assertions(int value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/Int64Assertions.cs b/Src/FluentAssertions/Numeric/Int64Assertions.cs index b3f4cd8650..c733dd294e 100644 --- a/Src/FluentAssertions/Numeric/Int64Assertions.cs +++ b/Src/FluentAssertions/Numeric/Int64Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class Int64Assertions : NumericAssertions { - internal Int64Assertions(long value) - : base(value) + internal Int64Assertions(long value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableByteAssertions.cs b/Src/FluentAssertions/Numeric/NullableByteAssertions.cs index 42001910fc..9fa6a4c4f0 100644 --- a/Src/FluentAssertions/Numeric/NullableByteAssertions.cs +++ b/Src/FluentAssertions/Numeric/NullableByteAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableByteAssertions : NullableNumericAssertions { - internal NullableByteAssertions(byte? value) - : base(value) + internal NullableByteAssertions(byte? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableDecimalAssertions.cs b/Src/FluentAssertions/Numeric/NullableDecimalAssertions.cs index 3a2897eb16..d5b27880c6 100644 --- a/Src/FluentAssertions/Numeric/NullableDecimalAssertions.cs +++ b/Src/FluentAssertions/Numeric/NullableDecimalAssertions.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -10,8 +11,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableDecimalAssertions : NullableNumericAssertions { - internal NullableDecimalAssertions(decimal? value) - : base(value) + internal NullableDecimalAssertions(decimal? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableDoubleAssertions.cs b/Src/FluentAssertions/Numeric/NullableDoubleAssertions.cs index 62c2c34a42..97af5f97bc 100644 --- a/Src/FluentAssertions/Numeric/NullableDoubleAssertions.cs +++ b/Src/FluentAssertions/Numeric/NullableDoubleAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableDoubleAssertions : NullableNumericAssertions { - internal NullableDoubleAssertions(double? value) - : base(value) + internal NullableDoubleAssertions(double? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableInt16Assertions.cs b/Src/FluentAssertions/Numeric/NullableInt16Assertions.cs index 89c5cc65d2..802d0c864d 100644 --- a/Src/FluentAssertions/Numeric/NullableInt16Assertions.cs +++ b/Src/FluentAssertions/Numeric/NullableInt16Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableInt16Assertions : NullableNumericAssertions { - internal NullableInt16Assertions(short? value) - : base(value) + internal NullableInt16Assertions(short? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableInt32Assertions.cs b/Src/FluentAssertions/Numeric/NullableInt32Assertions.cs index 67636c9ddf..7b2400568f 100644 --- a/Src/FluentAssertions/Numeric/NullableInt32Assertions.cs +++ b/Src/FluentAssertions/Numeric/NullableInt32Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableInt32Assertions : NullableNumericAssertions { - internal NullableInt32Assertions(int? value) - : base(value) + internal NullableInt32Assertions(int? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableInt64Assertions.cs b/Src/FluentAssertions/Numeric/NullableInt64Assertions.cs index 4630361178..1e5e04d386 100644 --- a/Src/FluentAssertions/Numeric/NullableInt64Assertions.cs +++ b/Src/FluentAssertions/Numeric/NullableInt64Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableInt64Assertions : NullableNumericAssertions { - internal NullableInt64Assertions(long? value) - : base(value) + internal NullableInt64Assertions(long? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableNumericAssertions.cs b/Src/FluentAssertions/Numeric/NullableNumericAssertions.cs index 5443d7f0a3..88af8fc2b3 100644 --- a/Src/FluentAssertions/Numeric/NullableNumericAssertions.cs +++ b/Src/FluentAssertions/Numeric/NullableNumericAssertions.cs @@ -11,8 +11,8 @@ namespace FluentAssertions.Numeric; public class NullableNumericAssertions : NullableNumericAssertions> where T : struct, IComparable { - public NullableNumericAssertions(T? value) - : base(value) + public NullableNumericAssertions(T? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -22,9 +22,12 @@ public class NullableNumericAssertions : NumericAssertions where TAssertions : NullableNumericAssertions { - public NullableNumericAssertions(T? value) - : base(value) + private readonly AssertionChain assertionChain; + + public NullableNumericAssertions(T? value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -39,7 +42,7 @@ public NullableNumericAssertions(T? value) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected a value{reason}."); @@ -74,7 +77,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect a value{reason}, but found {0}.", Subject); @@ -116,7 +119,7 @@ public AndConstraint Match(Expression> predicate, { Guard.ThrowIfArgumentIsNull(predicate); - Execute.Assertion + assertionChain .ForCondition(predicate.Compile()(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected value to match {0}{reason}, but found {1}.", predicate, Subject); diff --git a/Src/FluentAssertions/Numeric/NullableSByteAssertions.cs b/Src/FluentAssertions/Numeric/NullableSByteAssertions.cs index 76967bc17e..e83e756404 100644 --- a/Src/FluentAssertions/Numeric/NullableSByteAssertions.cs +++ b/Src/FluentAssertions/Numeric/NullableSByteAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableSByteAssertions : NullableNumericAssertions { - internal NullableSByteAssertions(sbyte? value) - : base(value) + internal NullableSByteAssertions(sbyte? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableSingleAssertions.cs b/Src/FluentAssertions/Numeric/NullableSingleAssertions.cs index fbcdaa919d..3a82df8969 100644 --- a/Src/FluentAssertions/Numeric/NullableSingleAssertions.cs +++ b/Src/FluentAssertions/Numeric/NullableSingleAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableSingleAssertions : NullableNumericAssertions { - internal NullableSingleAssertions(float? value) - : base(value) + internal NullableSingleAssertions(float? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableUInt16Assertions.cs b/Src/FluentAssertions/Numeric/NullableUInt16Assertions.cs index 97aad47a07..0df417d5e2 100644 --- a/Src/FluentAssertions/Numeric/NullableUInt16Assertions.cs +++ b/Src/FluentAssertions/Numeric/NullableUInt16Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableUInt16Assertions : NullableNumericAssertions { - internal NullableUInt16Assertions(ushort? value) - : base(value) + internal NullableUInt16Assertions(ushort? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableUInt32Assertions.cs b/Src/FluentAssertions/Numeric/NullableUInt32Assertions.cs index abf8f69801..69b94a8086 100644 --- a/Src/FluentAssertions/Numeric/NullableUInt32Assertions.cs +++ b/Src/FluentAssertions/Numeric/NullableUInt32Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableUInt32Assertions : NullableNumericAssertions { - internal NullableUInt32Assertions(uint? value) - : base(value) + internal NullableUInt32Assertions(uint? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NullableUInt64Assertions.cs b/Src/FluentAssertions/Numeric/NullableUInt64Assertions.cs index 1a2e44c3a7..6c36e3b703 100644 --- a/Src/FluentAssertions/Numeric/NullableUInt64Assertions.cs +++ b/Src/FluentAssertions/Numeric/NullableUInt64Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class NullableUInt64Assertions : NullableNumericAssertions { - internal NullableUInt64Assertions(ulong? value) - : base(value) + internal NullableUInt64Assertions(ulong? value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/NumericAssertions.cs b/Src/FluentAssertions/Numeric/NumericAssertions.cs index 81cf130b6f..ea1ffac010 100644 --- a/Src/FluentAssertions/Numeric/NumericAssertions.cs +++ b/Src/FluentAssertions/Numeric/NumericAssertions.cs @@ -16,8 +16,8 @@ namespace FluentAssertions.Numeric; public class NumericAssertions : NumericAssertions> where T : struct, IComparable { - public NumericAssertions(T value) - : base(value) + public NumericAssertions(T value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -32,14 +32,15 @@ public class NumericAssertions where T : struct, IComparable where TAssertions : NumericAssertions { - public NumericAssertions(T value) - : this((T?)value) + public NumericAssertions(T value, AssertionChain assertionChain) + : this((T?)value, assertionChain) { } - private protected NumericAssertions(T? value) + private protected NumericAssertions(T? value, AssertionChain assertionChain) { Subject = value; + CurrentAssertionChain = assertionChain; } public T? Subject { get; } @@ -57,7 +58,7 @@ private protected NumericAssertions(T? value) /// public AndConstraint Be(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject?.CompareTo(expected) == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be {0}{reason}, but found {1}" + GenerateDifferenceMessage(expected), expected, @@ -79,7 +80,7 @@ public AndConstraint Be(T expected, [StringSyntax("CompositeFormat" /// public AndConstraint Be(T? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(expected is { } value ? Subject?.CompareTo(value) == 0 : !Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be {0}{reason}, but found {1}" + GenerateDifferenceMessage(expected), expected, @@ -101,7 +102,7 @@ public AndConstraint Be(T? expected, [StringSyntax("CompositeFormat /// public AndConstraint NotBe(T unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject?.CompareTo(unexpected) != 0) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:value} to be {0}{reason}.", unexpected); @@ -122,7 +123,7 @@ public AndConstraint NotBe(T unexpected, [StringSyntax("CompositeFo /// public AndConstraint NotBe(T? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(unexpected is { } value ? Subject?.CompareTo(value) != 0 : Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:value} to be {0}{reason}.", unexpected); @@ -142,7 +143,7 @@ public AndConstraint NotBe(T? unexpected, [StringSyntax("CompositeF /// public AndConstraint BePositive([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject?.CompareTo(default) > 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be positive{reason}, but found {0}.", Subject); @@ -162,7 +163,7 @@ public AndConstraint BePositive([StringSyntax("CompositeFormat")] s /// public AndConstraint BeNegative([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is { } value && !IsNaN(value) && value.CompareTo(default) < 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be negative{reason}, but found {0}.", Subject); @@ -188,7 +189,7 @@ public AndConstraint BeLessThan(T expected, [StringSyntax("Composit throw new ArgumentException("A value can never be less than NaN", nameof(expected)); } - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is { } value && !IsNaN(value) && value.CompareTo(expected) < 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be less than {0}{reason}, but found {1}" + GenerateDifferenceMessage(expected), @@ -216,7 +217,7 @@ public AndConstraint BeLessThanOrEqualTo(T expected, throw new ArgumentException("A value can never be less than or equal to NaN", nameof(expected)); } - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is { } value && !IsNaN(value) && value.CompareTo(expected) <= 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -245,7 +246,7 @@ public AndConstraint BeGreaterThan(T expected, throw new ArgumentException("A value can never be greater than NaN", nameof(expected)); } - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject?.CompareTo(expected) > 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -274,7 +275,7 @@ public AndConstraint BeGreaterThanOrEqualTo(T expected, throw new ArgumentException("A value can never be greater than or equal to a NaN", nameof(expected)); } - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject?.CompareTo(expected) >= 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -311,7 +312,7 @@ public AndConstraint BeInRange(T minimumValue, T maximumValue, throw new ArgumentException("A range cannot begin or end with NaN"); } - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is { } value && value.CompareTo(minimumValue) >= 0 && value.CompareTo(maximumValue) <= 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be between {0} and {1}{reason}, but found {2}.", @@ -347,7 +348,7 @@ public AndConstraint NotBeInRange(T minimumValue, T maximumValue, throw new ArgumentException("A range cannot begin or end with NaN"); } - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is { } value && !(value.CompareTo(minimumValue) >= 0 && value.CompareTo(maximumValue) <= 0)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to not be between {0} and {1}{reason}, but found {2}.", @@ -383,7 +384,7 @@ public AndConstraint BeOneOf(params T[] validValues) public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is { } value && validValues.Contains(value)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be one of {0}{reason}, but found {1}.", validValues, Subject); @@ -441,12 +442,12 @@ public AndConstraint NotBeOfType(Type unexpectedType, [StringSyntax { Guard.ThrowIfArgumentIsNull(unexpectedType); - bool success = Execute.Assertion + CurrentAssertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected type not to be " + unexpectedType + "{reason}, but found ."); - if (success) + if (CurrentAssertionChain.Succeeded) { Subject.GetType().Should().NotBe(unexpectedType, because, becauseArgs); } @@ -473,7 +474,7 @@ public AndConstraint Match(Expression> predicate, { Guard.ThrowIfArgumentIsNull(predicate); - Execute.Assertion + CurrentAssertionChain .ForCondition(predicate.Compile()((T)Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to match {0}{reason}, but found {1}.", predicate.Body, Subject); @@ -510,4 +511,6 @@ private string GenerateDifferenceMessage(T? expected) var difference = CalculateDifferenceForFailureMessage(subject, expectedValue); return difference is null ? noDifferenceMessage : $" (difference of {difference})."; } + + public AssertionChain CurrentAssertionChain { get; } } diff --git a/Src/FluentAssertions/Numeric/SByteAssertions.cs b/Src/FluentAssertions/Numeric/SByteAssertions.cs index f0f19c4cda..7ac217e8a3 100644 --- a/Src/FluentAssertions/Numeric/SByteAssertions.cs +++ b/Src/FluentAssertions/Numeric/SByteAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class SByteAssertions : NumericAssertions { - internal SByteAssertions(sbyte value) - : base(value) + internal SByteAssertions(sbyte value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/SingleAssertions.cs b/Src/FluentAssertions/Numeric/SingleAssertions.cs index 6a11220314..2276b01334 100644 --- a/Src/FluentAssertions/Numeric/SingleAssertions.cs +++ b/Src/FluentAssertions/Numeric/SingleAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class SingleAssertions : NumericAssertions { - internal SingleAssertions(float value) - : base(value) + internal SingleAssertions(float value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/UInt16Assertions.cs b/Src/FluentAssertions/Numeric/UInt16Assertions.cs index b067a2a085..c1672f1482 100644 --- a/Src/FluentAssertions/Numeric/UInt16Assertions.cs +++ b/Src/FluentAssertions/Numeric/UInt16Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class UInt16Assertions : NumericAssertions { - internal UInt16Assertions(ushort value) - : base(value) + internal UInt16Assertions(ushort value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/UInt32Assertions.cs b/Src/FluentAssertions/Numeric/UInt32Assertions.cs index ecd089901d..49bcaad48f 100644 --- a/Src/FluentAssertions/Numeric/UInt32Assertions.cs +++ b/Src/FluentAssertions/Numeric/UInt32Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class UInt32Assertions : NumericAssertions { - internal UInt32Assertions(uint value) - : base(value) + internal UInt32Assertions(uint value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/Numeric/UInt64Assertions.cs b/Src/FluentAssertions/Numeric/UInt64Assertions.cs index f45d9f35d4..3060a1c012 100644 --- a/Src/FluentAssertions/Numeric/UInt64Assertions.cs +++ b/Src/FluentAssertions/Numeric/UInt64Assertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Globalization; +using FluentAssertions.Execution; namespace FluentAssertions.Numeric; @@ -9,8 +10,8 @@ namespace FluentAssertions.Numeric; [DebuggerNonUserCode] internal class UInt64Assertions : NumericAssertions { - internal UInt64Assertions(ulong value) - : base(value) + internal UInt64Assertions(ulong value, AssertionChain assertionChain) + : base(value, assertionChain) { } diff --git a/Src/FluentAssertions/NumericAssertionsExtensions.cs b/Src/FluentAssertions/NumericAssertionsExtensions.cs index a2ab750506..20d3b9e6fe 100644 --- a/Src/FluentAssertions/NumericAssertionsExtensions.cs +++ b/Src/FluentAssertions/NumericAssertionsExtensions.cs @@ -49,8 +49,9 @@ public static AndConstraint> BeCloseTo(this NumericAsse maxValue = sbyte.MaxValue; } - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, - becauseArgs); + FailIfValueOutsideBounds(parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, + nearbyValue, delta, actualValue, because, becauseArgs); return new AndConstraint>(parent); } @@ -91,8 +92,9 @@ public static AndConstraint> BeCloseTo(this NumericAsser maxValue = byte.MaxValue; } - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, - becauseArgs); + FailIfValueOutsideBounds(parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -133,8 +135,10 @@ public static AndConstraint> BeCloseTo(this NumericAsse maxValue = short.MaxValue; } - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, - becauseArgs); + FailIfValueOutsideBounds(parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, + nearbyValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -175,8 +179,10 @@ public static AndConstraint> BeCloseTo(this NumericAss maxValue = ushort.MaxValue; } - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, - becauseArgs); + FailIfValueOutsideBounds(parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, + nearbyValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -217,7 +223,10 @@ public static AndConstraint> BeCloseTo(this NumericAssert maxValue = int.MaxValue; } - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, + FailIfValueOutsideBounds(parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, + nearbyValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); @@ -259,8 +268,11 @@ public static AndConstraint> BeCloseTo(this NumericAsser maxValue = uint.MaxValue; } - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, - becauseArgs); + FailIfValueOutsideBounds( + parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, + nearbyValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -290,8 +302,10 @@ public static AndConstraint> BeCloseTo(this NumericAsser long minValue = GetMinValue(nearbyValue, delta); long maxValue = GetMaxValue(nearbyValue, delta); - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, - becauseArgs); + FailIfValueOutsideBounds(parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, + nearbyValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -332,17 +346,19 @@ public static AndConstraint> BeCloseTo(this NumericAsse maxValue = ulong.MaxValue; } - FailIfValueOutsideBounds(minValue <= actualValue && actualValue <= maxValue, nearbyValue, delta, actualValue, because, - becauseArgs); + FailIfValueOutsideBounds(parent.CurrentAssertionChain, + minValue <= actualValue && actualValue <= maxValue, + nearbyValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } - private static void FailIfValueOutsideBounds(bool valueWithinBounds, + private static void FailIfValueOutsideBounds(AssertionChain assertionChain, bool valueWithinBounds, TValue nearbyValue, TDelta delta, TValue actualValue, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(valueWithinBounds) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be within {0} from {1}{reason}, but found {2}.", @@ -389,8 +405,10 @@ public static AndConstraint> NotBeCloseTo(this NumericA maxValue = sbyte.MaxValue; } - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, - becauseArgs); + FailIfValueInsideBounds(parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), + distantValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -431,8 +449,11 @@ public static AndConstraint> NotBeCloseTo(this NumericAs maxValue = byte.MaxValue; } - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, - becauseArgs); + FailIfValueInsideBounds( + parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), + distantValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -473,8 +494,11 @@ public static AndConstraint> NotBeCloseTo(this NumericA maxValue = short.MaxValue; } - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, - becauseArgs); + FailIfValueInsideBounds( + parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), + distantValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -515,8 +539,10 @@ public static AndConstraint> NotBeCloseTo(this Numeric maxValue = ushort.MaxValue; } - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, - becauseArgs); + FailIfValueInsideBounds(parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), + distantValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -557,8 +583,11 @@ public static AndConstraint> NotBeCloseTo(this NumericAss maxValue = int.MaxValue; } - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, - becauseArgs); + FailIfValueInsideBounds( + parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), + distantValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -599,8 +628,10 @@ public static AndConstraint> NotBeCloseTo(this NumericAs maxValue = uint.MaxValue; } - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, - becauseArgs); + FailIfValueInsideBounds(parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), + distantValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -630,8 +661,10 @@ public static AndConstraint> NotBeCloseTo(this NumericAs long minValue = GetMinValue(distantValue, delta); long maxValue = GetMaxValue(distantValue, delta); - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, - becauseArgs); + FailIfValueInsideBounds(parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), + distantValue, delta, actualValue, + because, becauseArgs); return new AndConstraint>(parent); } @@ -672,18 +705,23 @@ public static AndConstraint> NotBeCloseTo(this NumericA maxValue = ulong.MaxValue; } - FailIfValueInsideBounds(!(minValue <= actualValue && actualValue <= maxValue), distantValue, delta, actualValue, because, + FailIfValueInsideBounds(parent.CurrentAssertionChain, + !(minValue <= actualValue && actualValue <= maxValue), distantValue, + delta, + actualValue, + because, becauseArgs); return new AndConstraint>(parent); } private static void FailIfValueInsideBounds( + AssertionChain assertionChain, bool valueOutsideBounds, TValue distantValue, TDelta delta, TValue actualValue, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(valueOutsideBounds) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:value} to be within {0} from {1}{reason}, but found {2}.", @@ -718,15 +756,17 @@ public static AndConstraint> BeApproximately(th { Guard.ThrowIfArgumentIsNegative(precision); - bool success = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(parent.Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was .", expectedValue, precision); - if (success) + if (assertion.Succeeded) { - var nonNullableAssertions = new SingleAssertions(parent.Subject.Value); + var nonNullableAssertions = new SingleAssertions(parent.Subject.Value, assertion); nonNullableAssertions.BeApproximately(expectedValue, precision, because, becauseArgs); } @@ -763,13 +803,15 @@ public static AndConstraint> BeApproximately(th return new AndConstraint>(parent); } - bool succeeded = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(expectedValue is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was {2}.", expectedValue, precision, parent.Subject); - if (succeeded) + if (assertion.Succeeded) { // ReSharper disable once PossibleInvalidOperationException parent.BeApproximately(expectedValue.Value, precision, because, becauseArgs); @@ -852,15 +894,17 @@ public static AndConstraint> BeApproximately(t { Guard.ThrowIfArgumentIsNegative(precision); - bool success = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(parent.Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was .", expectedValue, precision); - if (success) + if (assertion.Succeeded) { - var nonNullableAssertions = new DoubleAssertions(parent.Subject.Value); + var nonNullableAssertions = new DoubleAssertions(parent.Subject.Value, assertion); BeApproximately(nonNullableAssertions, expectedValue, precision, because, becauseArgs); } @@ -897,13 +941,15 @@ public static AndConstraint> BeApproximately(t return new AndConstraint>(parent); } - bool succeeded = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(expectedValue is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was {2}.", expectedValue, precision, parent.Subject); - if (succeeded) + if (assertion.Succeeded) { // ReSharper disable once PossibleInvalidOperationException parent.BeApproximately(expectedValue.Value, precision, because, becauseArgs); @@ -987,15 +1033,17 @@ public static AndConstraint> BeApproximately( { Guard.ThrowIfArgumentIsNegative(precision); - bool success = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(parent.Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was .", expectedValue, precision); - if (success) + if (assertion.Succeeded) { - var nonNullableAssertions = new DecimalAssertions(parent.Subject.Value); + var nonNullableAssertions = new DecimalAssertions(parent.Subject.Value, assertion); BeApproximately(nonNullableAssertions, expectedValue, precision, because, becauseArgs); } @@ -1033,13 +1081,15 @@ public static AndConstraint> BeApproximately( return new AndConstraint>(parent); } - bool succeeded = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(expectedValue is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to approximate {0} +/- {1}{reason}, but it was {2}.", expectedValue, precision, parent.Subject); - if (succeeded) + if (assertion.Succeeded) { // ReSharper disable once PossibleInvalidOperationException parent.BeApproximately(expectedValue.Value, precision, because, becauseArgs); @@ -1086,7 +1136,9 @@ private static void FailIfDifferenceOutsidePrecision( [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) where T : struct, IComparable { - Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(differenceWithinPrecision) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to approximate {1} +/- {2}{reason}, but {0} differed by {3}.", @@ -1123,7 +1175,7 @@ public static AndConstraint> NotBeApproximately if (parent.Subject is not null) { - var nonNullableAssertions = new SingleAssertions(parent.Subject.Value); + var nonNullableAssertions = new SingleAssertions(parent.Subject.Value, parent.CurrentAssertionChain); nonNullableAssertions.NotBeApproximately(unexpectedValue, precision, because, becauseArgs); } @@ -1160,13 +1212,15 @@ public static AndConstraint> NotBeApproximately return new AndConstraint>(parent); } - bool succeeded = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(parent.Subject is not null && unexpectedValue is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to not approximate {0} +/- {1}{reason}, but it was {2}.", unexpectedValue, precision, parent.Subject); - if (succeeded) + if (assertion.Succeeded) { // ReSharper disable once PossibleInvalidOperationException parent.NotBeApproximately(unexpectedValue.Value, precision, because, becauseArgs); @@ -1252,7 +1306,7 @@ public static AndConstraint> NotBeApproximatel if (parent.Subject is not null) { - var nonNullableAssertions = new DoubleAssertions(parent.Subject.Value); + var nonNullableAssertions = new DoubleAssertions(parent.Subject.Value, parent.CurrentAssertionChain); nonNullableAssertions.NotBeApproximately(unexpectedValue, precision, because, becauseArgs); } @@ -1290,13 +1344,15 @@ public static AndConstraint> NotBeApproximatel return new AndConstraint>(parent); } - bool succeeded = Execute.Assertion + AssertionChain assertionChain = parent.CurrentAssertionChain; + + assertionChain .ForCondition(parent.Subject is not null && unexpectedValue is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to not approximate {0} +/- {1}{reason}, but it was {2}.", unexpectedValue, precision, parent.Subject); - if (succeeded) + if (assertionChain.Succeeded) { // ReSharper disable once PossibleInvalidOperationException parent.NotBeApproximately(unexpectedValue.Value, precision, because, becauseArgs); @@ -1382,7 +1438,7 @@ public static AndConstraint> NotBeApproximate if (parent.Subject is not null) { - var nonNullableAssertions = new DecimalAssertions(parent.Subject.Value); + var nonNullableAssertions = new DecimalAssertions(parent.Subject.Value, parent.CurrentAssertionChain); NotBeApproximately(nonNullableAssertions, unexpectedValue, precision, because, becauseArgs); } @@ -1420,13 +1476,15 @@ public static AndConstraint> NotBeApproximate return new AndConstraint>(parent); } - bool succeeded = Execute.Assertion + var assertion = parent.CurrentAssertionChain; + + assertion .ForCondition(parent.Subject is not null && unexpectedValue is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to not approximate {0} +/- {1}{reason}, but it was {2}.", unexpectedValue, precision, parent.Subject); - if (succeeded) + if (assertion.Succeeded) { // ReSharper disable once PossibleInvalidOperationException parent.NotBeApproximately(unexpectedValue.Value, precision, because, becauseArgs); @@ -1473,7 +1531,7 @@ private static void FailIfDifferenceWithinPrecision( [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) where T : struct, IComparable { - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(differenceOutsidePrecision) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to not approximate {1} +/- {2}{reason}, but {0} only differed by {3}.", @@ -1500,7 +1558,7 @@ public static AndConstraint> BeNaN(this NumericAssertio { float actualValue = parent.Subject.Value; - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(float.IsNaN(actualValue)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be NaN{reason}, but found {0}.", actualValue); @@ -1524,7 +1582,7 @@ public static AndConstraint> BeNaN(this NumericAsserti { double actualValue = parent.Subject.Value; - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(double.IsNaN(actualValue)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be NaN{reason}, but found {0}.", actualValue); @@ -1548,7 +1606,7 @@ public static AndConstraint> BeNaN(this Nullabl { float? actualValue = parent.Subject; - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(actualValue is { } value && float.IsNaN(value)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be NaN{reason}, but found {0}.", actualValue); @@ -1572,7 +1630,7 @@ public static AndConstraint> BeNaN(this Nullab { double? actualValue = parent.Subject; - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(actualValue is { } value && double.IsNaN(value)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:value} to be NaN{reason}, but found {0}.", actualValue); @@ -1600,7 +1658,7 @@ public static AndConstraint> NotBeNaN(this NumericAsser { float actualValue = parent.Subject.Value; - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(!float.IsNaN(actualValue)) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:value} to be NaN{reason}."); @@ -1624,7 +1682,7 @@ public static AndConstraint> NotBeNaN(this NumericAsse { double actualValue = parent.Subject.Value; - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(!double.IsNaN(actualValue)) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:value} to be NaN{reason}."); @@ -1649,7 +1707,7 @@ public static AndConstraint> NotBeNaN(this Null float? actualValue = parent.Subject; bool actualValueIsNaN = actualValue is { } value && float.IsNaN(value); - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(!actualValueIsNaN) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:value} to be NaN{reason}."); @@ -1674,7 +1732,7 @@ public static AndConstraint> NotBeNaN(this Nul double? actualValue = parent.Subject; bool actualValueIsNaN = actualValue is { } value && double.IsNaN(value); - Execute.Assertion + parent.CurrentAssertionChain .ForCondition(!actualValueIsNaN) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:value} to be NaN{reason}."); @@ -1687,6 +1745,7 @@ public static AndConstraint> NotBeNaN(this Nul private static long GetMinValue(long value, ulong delta) { long minValue; + if (delta <= (ulong.MaxValue / 2)) { minValue = value - (long)delta; @@ -1711,6 +1770,7 @@ private static long GetMinValue(long value, ulong delta) private static long GetMaxValue(long value, ulong delta) { long maxValue; + if (delta <= (ulong.MaxValue / 2)) { maxValue = value + (long)delta; diff --git a/Src/FluentAssertions/ObjectAssertionsExtensions.cs b/Src/FluentAssertions/ObjectAssertionsExtensions.cs index 7819357665..5f93f280fa 100644 --- a/Src/FluentAssertions/ObjectAssertionsExtensions.cs +++ b/Src/FluentAssertions/ObjectAssertionsExtensions.cs @@ -5,7 +5,6 @@ using System.Xml.Serialization; using FluentAssertions.Common; using FluentAssertions.Equivalency; -using FluentAssertions.Execution; using FluentAssertions.Primitives; namespace FluentAssertions; @@ -66,7 +65,7 @@ public static AndConstraint BeDataContractSerializable(this } catch (Exception exc) { - Execute.Assertion + assertions.CurrentAssertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {0} to be serializable{reason}, but serialization failed with:" + Environment.NewLine + Environment.NewLine + "{1}.", @@ -110,7 +109,7 @@ public static AndConstraint BeXmlSerializable(this ObjectAsser } catch (Exception exc) { - Execute.Assertion + assertions.CurrentAssertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected {0} to be serializable{reason}, but serialization failed with:" + Environment.NewLine + Environment.NewLine + "{1}.", diff --git a/Src/FluentAssertions/OccurrenceConstraint.cs b/Src/FluentAssertions/OccurrenceConstraint.cs index 1f93dd0f13..347df0b750 100644 --- a/Src/FluentAssertions/OccurrenceConstraint.cs +++ b/Src/FluentAssertions/OccurrenceConstraint.cs @@ -1,6 +1,5 @@ using System; using FluentAssertions.Common; -using FluentAssertions.Execution; namespace FluentAssertions; @@ -22,8 +21,8 @@ protected OccurrenceConstraint(int expectedCount) internal abstract bool Assert(int actual); - internal void RegisterReportables(AssertionScope scope) + internal void RegisterContextData(Action register) { - scope.AddReportable("expectedOccurrence", $"{Mode} {ExpectedCount.Times()}"); + register("expectedOccurrence", $"{Mode} {ExpectedCount.Times()}"); } } diff --git a/Src/FluentAssertions/Primitives/BooleanAssertions.cs b/Src/FluentAssertions/Primitives/BooleanAssertions.cs index 1adcba679f..4932df3e13 100644 --- a/Src/FluentAssertions/Primitives/BooleanAssertions.cs +++ b/Src/FluentAssertions/Primitives/BooleanAssertions.cs @@ -12,8 +12,8 @@ namespace FluentAssertions.Primitives; public class BooleanAssertions : BooleanAssertions { - public BooleanAssertions(bool? value) - : base(value) + public BooleanAssertions(bool? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -27,8 +27,11 @@ public BooleanAssertions(bool? value) public class BooleanAssertions where TAssertions : BooleanAssertions { - public BooleanAssertions(bool? value) + private readonly AssertionChain assertionChain; + + public BooleanAssertions(bool? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -49,7 +52,7 @@ public BooleanAssertions(bool? value) /// public AndConstraint BeFalse([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == false) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:boolean} to be {0}{reason}, but found {1}.", false, Subject); @@ -69,7 +72,7 @@ public AndConstraint BeFalse([StringSyntax("CompositeFormat")] stri /// public AndConstraint BeTrue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == true) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:boolean} to be {0}{reason}, but found {1}.", true, Subject); @@ -88,9 +91,10 @@ public AndConstraint BeTrue([StringSyntax("CompositeFormat")] strin /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Be(bool expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Be(bool expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:boolean} to be {0}{reason}, but found {1}.", expected, Subject); @@ -109,9 +113,10 @@ public AndConstraint Be(bool expected, [StringSyntax("CompositeForm /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBe(bool unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBe(bool unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:boolean} not to be {0}{reason}, but found {1}.", unexpected, Subject); @@ -136,16 +141,15 @@ public AndConstraint Imply(bool consequent, { bool? antecedent = Subject; - Execute.Assertion + assertionChain .ForCondition(antecedent is not null) .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:antecedent} ({0}) to imply consequent ({1}){reason}, ", antecedent, consequent) - .FailWith("but found null.") - .Then - .ForCondition(!antecedent.Value || consequent) - .FailWith("but it did not.") - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:antecedent} ({0}) to imply consequent ({1}){reason}, ", antecedent, consequent, + chain => chain + .FailWith("but found null.") + .Then + .ForCondition(!antecedent.Value || consequent) + .FailWith("but it did not.")); return new AndConstraint((TAssertions)this); } diff --git a/Src/FluentAssertions/Primitives/DateOnlyAssertions.cs b/Src/FluentAssertions/Primitives/DateOnlyAssertions.cs index 5adc26841c..c5e57b5d73 100644 --- a/Src/FluentAssertions/Primitives/DateOnlyAssertions.cs +++ b/Src/FluentAssertions/Primitives/DateOnlyAssertions.cs @@ -15,8 +15,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class DateOnlyAssertions : DateOnlyAssertions { - public DateOnlyAssertions(DateOnly? value) - : base(value) + public DateOnlyAssertions(DateOnly? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -30,8 +30,11 @@ public DateOnlyAssertions(DateOnly? value) public class DateOnlyAssertions where TAssertions : DateOnlyAssertions { - public DateOnlyAssertions(DateOnly? value) + private readonly AssertionChain assertionChain; + + public DateOnlyAssertions(DateOnly? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -51,9 +54,10 @@ public DateOnlyAssertions(DateOnly? value) /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Be(DateOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Be(DateOnly expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} to be {0}{reason}, but found {1}.", @@ -73,9 +77,10 @@ public AndConstraint Be(DateOnly expected, [StringSyntax("Composite /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Be(DateOnly? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Be(DateOnly? expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} to be {0}{reason}, but found {1}.", @@ -98,7 +103,7 @@ public AndConstraint Be(DateOnly? expected, [StringSyntax("Composit public AndConstraint NotBe(DateOnly unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} not to be {0}{reason}, but it is.", unexpected); @@ -120,7 +125,7 @@ public AndConstraint NotBe(DateOnly unexpected, [StringSyntax("Comp public AndConstraint NotBe(DateOnly? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} not to be {0}{reason}, but it is.", unexpected); @@ -142,7 +147,7 @@ public AndConstraint NotBe(DateOnly? unexpected, public AndConstraint BeBefore(DateOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject < expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} to be before {0}{reason}, but found {1}.", expected, @@ -182,7 +187,7 @@ public AndConstraint NotBeBefore(DateOnly unexpected, public AndConstraint BeOnOrBefore(DateOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject <= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} to be on or before {0}{reason}, but found {1}.", expected, @@ -222,7 +227,7 @@ public AndConstraint NotBeOnOrBefore(DateOnly unexpected, public AndConstraint BeAfter(DateOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject > expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} to be after {0}{reason}, but found {1}.", expected, @@ -262,7 +267,7 @@ public AndConstraint NotBeAfter(DateOnly unexpected, public AndConstraint BeOnOrAfter(DateOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject >= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} to be on or after {0}{reason}, but found {1}.", expected, @@ -299,18 +304,17 @@ public AndConstraint NotBeOnOrAfter(DateOnly unexpected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveYear(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveYear(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the year part of {context:the date} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found .") - .Then - .ForCondition(Subject.Value.Year == expected) - .FailWith(", but found {0}.", Subject.Value.Year) - .Then - .ClearExpectation(); + .WithExpectation("Expected the year part of {context:the date} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found .") + .Then + .ForCondition(Subject.Value.Year == expected) + .FailWith(", but found {0}.", Subject.Value.Year)); return new AndConstraint((TAssertions)this); } @@ -326,9 +330,10 @@ public AndConstraint HaveYear(int expected, [StringSyntax("Composit /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveYear(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveYear(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.HasValue) .FailWith("Did not expect the year part of {context:the date} to be {0}{reason}, but found a DateOnly.", @@ -352,18 +357,17 @@ public AndConstraint NotHaveYear(int unexpected, [StringSyntax("Com /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveMonth(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveMonth(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the month part of {context:the date} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateOnly.") - .Then - .ForCondition(Subject.Value.Month == expected) - .FailWith(", but found {0}.", Subject.Value.Month) - .Then - .ClearExpectation(); + .WithExpectation("Expected the month part of {context:the date} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateOnly.") + .Then + .ForCondition(Subject.Value.Month == expected) + .FailWith(", but found {0}.", Subject.Value.Month)); return new AndConstraint((TAssertions)this); } @@ -379,18 +383,17 @@ public AndConstraint HaveMonth(int expected, [StringSyntax("Composi /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveMonth(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveMonth(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the month part of {context:the date} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateOnly.") - .Then - .ForCondition(Subject.Value.Month != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the month part of {context:the date} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateOnly.") + .Then + .ForCondition(Subject.Value.Month != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -406,18 +409,17 @@ public AndConstraint NotHaveMonth(int unexpected, [StringSyntax("Co /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveDay(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveDay(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the day part of {context:the date} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateOnly.") - .Then - .ForCondition(Subject.Value.Day == expected) - .FailWith(", but found {0}.", Subject.Value.Day) - .Then - .ClearExpectation(); + .WithExpectation("Expected the day part of {context:the date} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateOnly.") + .Then + .ForCondition(Subject.Value.Day == expected) + .FailWith(", but found {0}.", Subject.Value.Day)); return new AndConstraint((TAssertions)this); } @@ -433,18 +435,17 @@ public AndConstraint HaveDay(int expected, [StringSyntax("Composite /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveDay(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveDay(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the day part of {context:the date} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateOnly.") - .Then - .ForCondition(Subject.Value.Day != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the day part of {context:the date} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateOnly.") + .Then + .ForCondition(Subject.Value.Day != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -484,7 +485,8 @@ public AndConstraint BeOneOf(params DateOnly[] validValues) /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeOneOf(IEnumerable validValues, + [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { return BeOneOf(validValues.Cast(), because, becauseArgs); } @@ -505,7 +507,7 @@ public AndConstraint BeOneOf(IEnumerable validValues, [St public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(validValues.Contains(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date} to be one of {0}{reason}, but found {1}.", validValues, Subject); diff --git a/Src/FluentAssertions/Primitives/DateTimeAssertions.cs b/Src/FluentAssertions/Primitives/DateTimeAssertions.cs index 63effadf47..b1f7ffa01a 100644 --- a/Src/FluentAssertions/Primitives/DateTimeAssertions.cs +++ b/Src/FluentAssertions/Primitives/DateTimeAssertions.cs @@ -18,8 +18,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class DateTimeAssertions : DateTimeAssertions { - public DateTimeAssertions(DateTime? value) - : base(value) + public DateTimeAssertions(DateTime? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -37,8 +37,11 @@ public DateTimeAssertions(DateTime? value) public class DateTimeAssertions where TAssertions : DateTimeAssertions { - public DateTimeAssertions(DateTime? value) + private readonly AssertionChain assertionChain; + + public DateTimeAssertions(DateTime? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -58,9 +61,10 @@ public DateTimeAssertions(DateTime? value) /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Be(DateTime expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Be(DateTime expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date and time} to be {0}{reason}, but found {1}.", @@ -80,9 +84,10 @@ public AndConstraint Be(DateTime expected, [StringSyntax("Composite /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Be(DateTime? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Be(DateTime? expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date and time} to be {0}{reason}, but found {1}.", @@ -105,7 +110,7 @@ public AndConstraint Be(DateTime? expected, [StringSyntax("Composit public AndConstraint NotBe(DateTime unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date and time} not to be {0}{reason}, but it is.", unexpected); @@ -127,7 +132,7 @@ public AndConstraint NotBe(DateTime unexpected, public AndConstraint NotBe(DateTime? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date and time} not to be {0}{reason}, but it is.", unexpected); @@ -170,16 +175,15 @@ public AndConstraint BeCloseTo(DateTime nearbyTime, TimeSpan precis TimeSpan? difference = (Subject - nearbyTime)?.Duration(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:the date and time} to be within {0} from {1}{reason}", precision, nearbyTime) - .ForCondition(Subject is not null) - .FailWith(", but found .") - .Then - .ForCondition(Subject >= minimumValue && Subject <= maximumValue) - .FailWith(", but {0} was off by {1}.", Subject, difference) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:the date and time} to be within {0} from {1}{reason}", precision, nearbyTime, + chain => chain + .ForCondition(Subject is not null) + .FailWith(", but found .") + .Then + .ForCondition(Subject >= minimumValue && Subject <= maximumValue) + .FailWith(", but {0} was off by {1}.", Subject, difference)); return new AndConstraint((TAssertions)this); } @@ -217,7 +221,7 @@ public AndConstraint NotBeCloseTo(DateTime distantTime, TimeSpan pr long distanceToMaxInTicks = (DateTime.MaxValue - distantTime).Ticks; DateTime maximumValue = distantTime.AddTicks(Math.Min(precision.Ticks, distanceToMaxInTicks)); - Execute.Assertion + assertionChain .ForCondition(Subject < minimumValue || Subject > maximumValue) .BecauseOf(because, becauseArgs) .FailWith( @@ -242,7 +246,7 @@ public AndConstraint NotBeCloseTo(DateTime distantTime, TimeSpan pr public AndConstraint BeBefore(DateTime expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject < expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be before {0}{reason}, but found {1}.", expected, @@ -282,7 +286,7 @@ public AndConstraint NotBeBefore(DateTime unexpected, public AndConstraint BeOnOrBefore(DateTime expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject <= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be on or before {0}{reason}, but found {1}.", expected, @@ -322,7 +326,7 @@ public AndConstraint NotBeOnOrBefore(DateTime unexpected, public AndConstraint BeAfter(DateTime expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject > expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be after {0}{reason}, but found {1}.", expected, @@ -362,7 +366,7 @@ public AndConstraint NotBeAfter(DateTime unexpected, public AndConstraint BeOnOrAfter(DateTime expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject >= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be on or after {0}{reason}, but found {1}.", expected, @@ -399,18 +403,17 @@ public AndConstraint NotBeOnOrAfter(DateTime unexpected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveYear(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveYear(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the year part of {context:the date} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found .") - .Then - .ForCondition(Subject.Value.Year == expected) - .FailWith(", but found {0}.", Subject.Value.Year) - .Then - .ClearExpectation(); + .WithExpectation("Expected the year part of {context:the date} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found .") + .Then + .ForCondition(Subject.Value.Year == expected) + .FailWith(", but found {0}.", Subject.Value.Year)); return new AndConstraint((TAssertions)this); } @@ -426,9 +429,10 @@ public AndConstraint HaveYear(int expected, [StringSyntax("Composit /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveYear(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveYear(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.HasValue) .FailWith("Did not expect the year part of {context:the date} to be {0}{reason}, but found a DateTime.", @@ -452,18 +456,17 @@ public AndConstraint NotHaveYear(int unexpected, [StringSyntax("Com /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveMonth(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveMonth(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the month part of {context:the date} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Month == expected) - .FailWith(", but found {0}.", Subject.Value.Month) - .Then - .ClearExpectation(); + .WithExpectation("Expected the month part of {context:the date} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Month == expected) + .FailWith(", but found {0}.", Subject.Value.Month)); return new AndConstraint((TAssertions)this); } @@ -479,18 +482,17 @@ public AndConstraint HaveMonth(int expected, [StringSyntax("Composi /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveMonth(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveMonth(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the month part of {context:the date} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Month != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the month part of {context:the date} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Month != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -506,18 +508,17 @@ public AndConstraint NotHaveMonth(int unexpected, [StringSyntax("Co /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveDay(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveDay(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the day part of {context:the date} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Day == expected) - .FailWith(", but found {0}.", Subject.Value.Day) - .Then - .ClearExpectation(); + .WithExpectation("Expected the day part of {context:the date} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Day == expected) + .FailWith(", but found {0}.", Subject.Value.Day)); return new AndConstraint((TAssertions)this); } @@ -533,18 +534,17 @@ public AndConstraint HaveDay(int expected, [StringSyntax("Composite /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveDay(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveDay(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the day part of {context:the date} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Day != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the day part of {context:the date} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Day != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -560,18 +560,17 @@ public AndConstraint NotHaveDay(int unexpected, [StringSyntax("Comp /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveHour(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveHour(int expected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the hour part of {context:the time} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Hour == expected) - .FailWith(", but found {0}.", Subject.Value.Hour) - .Then - .ClearExpectation(); + .WithExpectation("Expected the hour part of {context:the time} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Hour == expected) + .FailWith(", but found {0}.", Subject.Value.Hour)); return new AndConstraint((TAssertions)this); } @@ -587,18 +586,17 @@ public AndConstraint HaveHour(int expected, [StringSyntax("Composit /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveHour(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveHour(int unexpected, [StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the hour part of {context:the time} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.", unexpected) - .Then - .ForCondition(Subject.Value.Hour != unexpected) - .FailWith(", but it was.", unexpected, Subject.Value.Hour) - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the hour part of {context:the time} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.", unexpected) + .Then + .ForCondition(Subject.Value.Hour != unexpected) + .FailWith(", but it was.", unexpected, Subject.Value.Hour)); return new AndConstraint((TAssertions)this); } @@ -617,16 +615,14 @@ public AndConstraint NotHaveHour(int unexpected, [StringSyntax("Com public AndConstraint HaveMinute(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the minute part of {context:the time} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Minute == expected) - .FailWith(", but found {0}.", Subject.Value.Minute) - .Then - .ClearExpectation(); + .WithExpectation("Expected the minute part of {context:the time} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Minute == expected) + .FailWith(", but found {0}.", Subject.Value.Minute)); return new AndConstraint((TAssertions)this); } @@ -645,16 +641,14 @@ public AndConstraint HaveMinute(int expected, public AndConstraint NotHaveMinute(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the minute part of {context:the time} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.", unexpected) - .Then - .ForCondition(Subject.Value.Minute != unexpected) - .FailWith(", but it was.", unexpected, Subject.Value.Minute) - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the minute part of {context:the time} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.", unexpected) + .Then + .ForCondition(Subject.Value.Minute != unexpected) + .FailWith(", but it was.", unexpected, Subject.Value.Minute)); return new AndConstraint((TAssertions)this); } @@ -673,16 +667,14 @@ public AndConstraint NotHaveMinute(int unexpected, public AndConstraint HaveSecond(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the seconds part of {context:the time} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Second == expected) - .FailWith(", but found {0}.", Subject.Value.Second) - .Then - .ClearExpectation(); + .WithExpectation("Expected the seconds part of {context:the time} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Second == expected) + .FailWith(", but found {0}.", Subject.Value.Second)); return new AndConstraint((TAssertions)this); } @@ -701,16 +693,14 @@ public AndConstraint HaveSecond(int expected, public AndConstraint NotHaveSecond(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the seconds part of {context:the time} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Second != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the seconds part of {context:the time} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Second != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -724,7 +714,8 @@ public AndConstraint NotHaveSecond(int unexpected, /// public DateTimeRangeAssertions BeMoreThan(TimeSpan timeSpan) { - return new DateTimeRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.MoreThan, timeSpan); + return new DateTimeRangeAssertions((TAssertions)this, assertionChain, Subject, TimeSpanCondition.MoreThan, + timeSpan); } /// @@ -737,7 +728,8 @@ public DateTimeRangeAssertions BeMoreThan(TimeSpan timeSpan) /// public DateTimeRangeAssertions BeAtLeast(TimeSpan timeSpan) { - return new DateTimeRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.AtLeast, timeSpan); + return new DateTimeRangeAssertions((TAssertions)this, assertionChain, Subject, TimeSpanCondition.AtLeast, + timeSpan); } /// @@ -749,7 +741,8 @@ public DateTimeRangeAssertions BeAtLeast(TimeSpan timeSpan) /// public DateTimeRangeAssertions BeExactly(TimeSpan timeSpan) { - return new DateTimeRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.Exactly, timeSpan); + return new DateTimeRangeAssertions((TAssertions)this, assertionChain, Subject, TimeSpanCondition.Exactly, + timeSpan); } /// @@ -761,7 +754,8 @@ public DateTimeRangeAssertions BeExactly(TimeSpan timeSpan) /// public DateTimeRangeAssertions BeWithin(TimeSpan timeSpan) { - return new DateTimeRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.Within, timeSpan); + return new DateTimeRangeAssertions((TAssertions)this, assertionChain, Subject, TimeSpanCondition.Within, + timeSpan); } /// @@ -773,7 +767,8 @@ public DateTimeRangeAssertions BeWithin(TimeSpan timeSpan) /// public DateTimeRangeAssertions BeLessThan(TimeSpan timeSpan) { - return new DateTimeRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.LessThan, timeSpan); + return new DateTimeRangeAssertions((TAssertions)this, assertionChain, Subject, TimeSpanCondition.LessThan, + timeSpan); } /// @@ -792,16 +787,15 @@ public AndConstraint BeSameDateAs(DateTime expected, { DateTime expectedDate = expected.Date; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the date part of {context:the date and time} to be {0}{reason}", expectedDate) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.", expectedDate) - .Then - .ForCondition(Subject.Value.Date == expectedDate) - .FailWith(", but found {1}.", expectedDate, Subject.Value) - .Then - .ClearExpectation(); + .WithExpectation("Expected the date part of {context:the date and time} to be {0}{reason}", expectedDate, + chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.", expectedDate) + .Then + .ForCondition(Subject.Value.Date == expectedDate) + .FailWith(", but found {1}.", expectedDate, Subject.Value)); return new AndConstraint((TAssertions)this); } @@ -822,16 +816,15 @@ public AndConstraint NotBeSameDateAs(DateTime unexpected, { DateTime unexpectedDate = unexpected.Date; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the date part of {context:the date and time} to be {0}{reason}", unexpectedDate) - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Date != unexpectedDate) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the date part of {context:the date and time} to be {0}{reason}", unexpectedDate, + chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Date != unexpectedDate) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -893,7 +886,7 @@ public AndConstraint BeOneOf(IEnumerable validValues, public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(validValues.Contains(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:date and time} to be one of {0}{reason}, but found {1}.", validValues, Subject); @@ -917,16 +910,14 @@ public AndConstraint BeOneOf(IEnumerable validValues, public AndConstraint BeIn(DateTimeKind expectedKind, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:the date and time} to be in " + expectedKind + "{reason}") - .ForCondition(Subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(Subject.Value.Kind == expectedKind) - .FailWith(", but found " + Subject.Value.Kind + ".") - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:the date and time} to be in " + expectedKind + "{reason}", chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(Subject.Value.Kind == expectedKind) + .FailWith(", but found " + Subject.Value.Kind + ".")); return new AndConstraint((TAssertions)this); } @@ -947,17 +938,15 @@ public AndConstraint BeIn(DateTimeKind expectedKind, public AndConstraint NotBeIn(DateTimeKind unexpectedKind, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect {context:the date and time} to be in " + unexpectedKind + "{reason}") - .Given(() => Subject) - .ForCondition(subject => subject.HasValue) - .FailWith(", but found a DateTime.") - .Then - .ForCondition(subject => subject.GetValueOrDefault().Kind != unexpectedKind) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect {context:the date and time} to be in " + unexpectedKind + "{reason}", chain => chain + .Given(() => Subject) + .ForCondition(subject => subject.HasValue) + .FailWith(", but found a DateTime.") + .Then + .ForCondition(subject => subject.GetValueOrDefault().Kind != unexpectedKind) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } diff --git a/Src/FluentAssertions/Primitives/DateTimeOffsetAssertions.cs b/Src/FluentAssertions/Primitives/DateTimeOffsetAssertions.cs index 35a03100c1..83d27c8ad0 100644 --- a/Src/FluentAssertions/Primitives/DateTimeOffsetAssertions.cs +++ b/Src/FluentAssertions/Primitives/DateTimeOffsetAssertions.cs @@ -19,8 +19,8 @@ namespace FluentAssertions.Primitives; public class DateTimeOffsetAssertions : DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(DateTimeOffset? value) - : base(value) + public DateTimeOffsetAssertions(DateTimeOffset? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -38,8 +38,11 @@ public DateTimeOffsetAssertions(DateTimeOffset? value) public class DateTimeOffsetAssertions where TAssertions : DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(DateTimeOffset? value) + private readonly AssertionChain assertionChain; + + public DateTimeOffsetAssertions(DateTimeOffset? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -62,17 +65,15 @@ public DateTimeOffsetAssertions(DateTimeOffset? value) public AndConstraint Be(DateTimeOffset expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .WithExpectation("Expected {context:the date and time} to represent the same point in time as {0}{reason}, ", - expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject == expected) - .FailWith("but {0} does not.", Subject) - .Then - .ClearExpectation(); + expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject == expected) + .FailWith("but {0} does not.", Subject)); return new AndConstraint((TAssertions)this); } @@ -93,24 +94,21 @@ public AndConstraint Be(DateTimeOffset? expected, { if (!expected.HasValue) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.HasValue) .FailWith("Expected {context:the date and time} to be {reason}, but it was {0}.", Subject); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .WithExpectation("Expected {context:the date and time} to represent the same point in time as {0}{reason}, ", - expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject == expected) - .FailWith("but {0} does not.", Subject) - .Then - .ClearExpectation(); + expected, chain => chain.ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject == expected) + .FailWith("but {0} does not.", Subject)); } return new AndConstraint((TAssertions)this); @@ -130,7 +128,7 @@ public AndConstraint Be(DateTimeOffset? expected, public AndConstraint NotBe(DateTimeOffset unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith( @@ -154,7 +152,7 @@ public AndConstraint NotBe(DateTimeOffset unexpected, public AndConstraint NotBe(DateTimeOffset? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith( @@ -178,16 +176,14 @@ public AndConstraint NotBe(DateTimeOffset? unexpected, public AndConstraint BeExactly(DateTimeOffset expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:the date and time} to be exactly {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.EqualsExact(expected)) - .FailWith("but it was {0}.", Subject) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:the date and time} to be exactly {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.EqualsExact(expected)) + .FailWith("but it was {0}.", Subject)); return new AndConstraint((TAssertions)this); } @@ -209,23 +205,21 @@ public AndConstraint BeExactly(DateTimeOffset? expected, { if (!expected.HasValue) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.HasValue) .FailWith("Expected {context:the date and time} to be {reason}, but it was {0}.", Subject); } else { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:the date and time} to be exactly {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.EqualsExact(expected.Value)) - .FailWith("but it was {0}.", Subject) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:the date and time} to be exactly {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.EqualsExact(expected.Value)) + .FailWith("but it was {0}.", Subject)); } return new AndConstraint((TAssertions)this); @@ -246,7 +240,7 @@ public AndConstraint BeExactly(DateTimeOffset? expected, public AndConstraint NotBeExactly(DateTimeOffset unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject?.EqualsExact(unexpected) != true) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:the date and time} to be exactly {0}{reason}, but it was.", unexpected); @@ -268,7 +262,7 @@ public AndConstraint NotBeExactly(DateTimeOffset unexpected, public AndConstraint NotBeExactly(DateTimeOffset? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!((Subject == null && unexpected == null) || (Subject != null && unexpected != null && Subject.Value.EqualsExact(unexpected.Value)))) .BecauseOf(because, becauseArgs) @@ -312,16 +306,15 @@ public AndConstraint BeCloseTo(DateTimeOffset nearbyTime, TimeSpan TimeSpan? difference = (Subject - nearbyTime)?.Duration(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:the date and time} to be within {0} from {1}{reason}", precision, nearbyTime) - .ForCondition(Subject is not null) - .FailWith(", but found .") - .Then - .ForCondition(Subject >= minimumValue && Subject <= maximumValue) - .FailWith(", but {0} was off by {1}.", Subject, difference) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:the date and time} to be within {0} from {1}{reason}", precision, nearbyTime, + chain => chain + .ForCondition(Subject is not null) + .FailWith(", but found .") + .Then + .ForCondition(Subject >= minimumValue && Subject <= maximumValue) + .FailWith(", but {0} was off by {1}.", Subject, difference)); return new AndConstraint((TAssertions)this); } @@ -359,7 +352,7 @@ public AndConstraint NotBeCloseTo(DateTimeOffset distantTime, TimeS long distanceToMaxInTicks = (DateTimeOffset.MaxValue - distantTime).Ticks; DateTimeOffset maximumValue = distantTime.AddTicks(Math.Min(precision.Ticks, distanceToMaxInTicks)); - Execute.Assertion + assertionChain .ForCondition(Subject < minimumValue || Subject > maximumValue) .BecauseOf(because, becauseArgs) .FailWith( @@ -384,7 +377,7 @@ public AndConstraint NotBeCloseTo(DateTimeOffset distantTime, TimeS public AndConstraint BeBefore(DateTimeOffset expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject < expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be before {0}{reason}, but it was {1}.", expected, @@ -424,7 +417,7 @@ public AndConstraint NotBeBefore(DateTimeOffset unexpected, public AndConstraint BeOnOrBefore(DateTimeOffset expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject <= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be on or before {0}{reason}, but it was {1}.", expected, @@ -464,7 +457,7 @@ public AndConstraint NotBeOnOrBefore(DateTimeOffset unexpected, public AndConstraint BeAfter(DateTimeOffset expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject > expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be after {0}{reason}, but it was {1}.", expected, @@ -504,7 +497,7 @@ public AndConstraint NotBeAfter(DateTimeOffset unexpected, public AndConstraint BeOnOrAfter(DateTimeOffset expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject >= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be on or after {0}{reason}, but it was {1}.", expected, @@ -544,16 +537,14 @@ public AndConstraint NotBeOnOrAfter(DateTimeOffset unexpected, public AndConstraint HaveYear(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the year part of {context:the date} to be {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Year == expected) - .FailWith("but it was {0}.", Subject.Value.Year) - .Then - .ClearExpectation(); + .WithExpectation("Expected the year part of {context:the date} to be {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Year == expected) + .FailWith("but it was {0}.", Subject.Value.Year)); return new AndConstraint((TAssertions)this); } @@ -572,16 +563,14 @@ public AndConstraint HaveYear(int expected, public AndConstraint NotHaveYear(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the year part of {context:the date} to be {0}{reason}, ", unexpected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Year != unexpected) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the year part of {context:the date} to be {0}{reason}, ", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Year != unexpected) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -600,16 +589,14 @@ public AndConstraint NotHaveYear(int unexpected, public AndConstraint HaveMonth(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the month part of {context:the date} to be {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Month == expected) - .FailWith("but it was {0}.", Subject.Value.Month) - .Then - .ClearExpectation(); + .WithExpectation("Expected the month part of {context:the date} to be {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Month == expected) + .FailWith("but it was {0}.", Subject.Value.Month)); return new AndConstraint((TAssertions)this); } @@ -628,16 +615,14 @@ public AndConstraint HaveMonth(int expected, public AndConstraint NotHaveMonth(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the month part of {context:the date} to be {0}{reason}, ", unexpected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Month != unexpected) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the month part of {context:the date} to be {0}{reason}, ", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Month != unexpected) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -656,16 +641,14 @@ public AndConstraint NotHaveMonth(int unexpected, public AndConstraint HaveDay(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the day part of {context:the date} to be {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Day == expected) - .FailWith("but it was {0}.", Subject.Value.Day) - .Then - .ClearExpectation(); + .WithExpectation("Expected the day part of {context:the date} to be {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Day == expected) + .FailWith("but it was {0}.", Subject.Value.Day)); return new AndConstraint((TAssertions)this); } @@ -684,16 +667,14 @@ public AndConstraint HaveDay(int expected, public AndConstraint NotHaveDay(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the day part of {context:the date} to be {0}{reason}, ", unexpected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Day != unexpected) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the day part of {context:the date} to be {0}{reason}, ", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Day != unexpected) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -712,16 +693,14 @@ public AndConstraint NotHaveDay(int unexpected, public AndConstraint HaveHour(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the hour part of {context:the time} to be {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Hour == expected) - .FailWith("but it was {0}.", Subject.Value.Hour) - .Then - .ClearExpectation(); + .WithExpectation("Expected the hour part of {context:the time} to be {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Hour == expected) + .FailWith("but it was {0}.", Subject.Value.Hour)); return new AndConstraint((TAssertions)this); } @@ -740,16 +719,14 @@ public AndConstraint HaveHour(int expected, public AndConstraint NotHaveHour(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the hour part of {context:the time} to be {0}{reason}, ", unexpected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Hour != unexpected) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the hour part of {context:the time} to be {0}{reason}, ", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Hour != unexpected) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -768,16 +745,14 @@ public AndConstraint NotHaveHour(int unexpected, public AndConstraint HaveMinute(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the minute part of {context:the time} to be {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Minute == expected) - .FailWith("but it was {0}.", Subject.Value.Minute) - .Then - .ClearExpectation(); + .WithExpectation("Expected the minute part of {context:the time} to be {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Minute == expected) + .FailWith("but it was {0}.", Subject.Value.Minute)); return new AndConstraint((TAssertions)this); } @@ -796,16 +771,15 @@ public AndConstraint HaveMinute(int expected, public AndConstraint NotHaveMinute(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the minute part of {context:the time} to be {0}{reason}, ", unexpected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Minute != unexpected) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the minute part of {context:the time} to be {0}{reason}, ", unexpected, + chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Minute != unexpected) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -824,16 +798,14 @@ public AndConstraint NotHaveMinute(int unexpected, public AndConstraint HaveSecond(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the seconds part of {context:the time} to be {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Second == expected) - .FailWith("but it was {0}.", Subject.Value.Second) - .Then - .ClearExpectation(); + .WithExpectation("Expected the seconds part of {context:the time} to be {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Second == expected) + .FailWith("but it was {0}.", Subject.Value.Second)); return new AndConstraint((TAssertions)this); } @@ -852,16 +824,15 @@ public AndConstraint HaveSecond(int expected, public AndConstraint NotHaveSecond(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the seconds part of {context:the time} to be {0}{reason}, ", unexpected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Second != unexpected) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the seconds part of {context:the time} to be {0}{reason}, ", unexpected, + chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Second != unexpected) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -880,16 +851,14 @@ public AndConstraint NotHaveSecond(int unexpected, public AndConstraint HaveOffset(TimeSpan expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the offset of {context:the date} to be {0}{reason}, ", expected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Offset == expected) - .FailWith("but it was {0}.", Subject.Value.Offset) - .Then - .ClearExpectation(); + .WithExpectation("Expected the offset of {context:the date} to be {0}{reason}, ", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Offset == expected) + .FailWith("but it was {0}.", Subject.Value.Offset)); return new AndConstraint((TAssertions)this); } @@ -908,16 +877,14 @@ public AndConstraint HaveOffset(TimeSpan expected, public AndConstraint NotHaveOffset(TimeSpan unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the offset of {context:the date} to be {0}{reason}, ", unexpected) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Offset != unexpected) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the offset of {context:the date} to be {0}{reason}, ", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Offset != unexpected) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -931,7 +898,8 @@ public AndConstraint NotHaveOffset(TimeSpan unexpected, /// public DateTimeOffsetRangeAssertions BeMoreThan(TimeSpan timeSpan) { - return new DateTimeOffsetRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.MoreThan, timeSpan); + return new DateTimeOffsetRangeAssertions((TAssertions)this, assertionChain, Subject, + TimeSpanCondition.MoreThan, timeSpan); } /// @@ -944,7 +912,8 @@ public DateTimeOffsetRangeAssertions BeMoreThan(TimeSpan timeSpan) /// public DateTimeOffsetRangeAssertions BeAtLeast(TimeSpan timeSpan) { - return new DateTimeOffsetRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.AtLeast, timeSpan); + return new DateTimeOffsetRangeAssertions((TAssertions)this, assertionChain, Subject, + TimeSpanCondition.AtLeast, timeSpan); } /// @@ -956,7 +925,8 @@ public DateTimeOffsetRangeAssertions BeAtLeast(TimeSpan timeSpan) /// public DateTimeOffsetRangeAssertions BeExactly(TimeSpan timeSpan) { - return new DateTimeOffsetRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.Exactly, timeSpan); + return new DateTimeOffsetRangeAssertions((TAssertions)this, assertionChain, Subject, + TimeSpanCondition.Exactly, timeSpan); } /// @@ -968,7 +938,8 @@ public DateTimeOffsetRangeAssertions BeExactly(TimeSpan timeSpan) /// public DateTimeOffsetRangeAssertions BeWithin(TimeSpan timeSpan) { - return new DateTimeOffsetRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.Within, timeSpan); + return new DateTimeOffsetRangeAssertions((TAssertions)this, assertionChain, Subject, + TimeSpanCondition.Within, timeSpan); } /// @@ -980,7 +951,8 @@ public DateTimeOffsetRangeAssertions BeWithin(TimeSpan timeSpan) /// public DateTimeOffsetRangeAssertions BeLessThan(TimeSpan timeSpan) { - return new DateTimeOffsetRangeAssertions((TAssertions)this, Subject, TimeSpanCondition.LessThan, timeSpan); + return new DateTimeOffsetRangeAssertions((TAssertions)this, assertionChain, Subject, + TimeSpanCondition.LessThan, timeSpan); } /// @@ -999,16 +971,15 @@ public AndConstraint BeSameDateAs(DateTimeOffset expected, { DateTime expectedDate = expected.Date; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the date part of {context:the date and time} to be {0}{reason}, ", expectedDate) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.", expectedDate) - .Then - .ForCondition(Subject.Value.Date == expectedDate) - .FailWith("but it was {0}.", Subject.Value.Date) - .Then - .ClearExpectation(); + .WithExpectation("Expected the date part of {context:the date and time} to be {0}{reason}, ", expectedDate, + chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.", expectedDate) + .Then + .ForCondition(Subject.Value.Date == expectedDate) + .FailWith("but it was {0}.", Subject.Value.Date)); return new AndConstraint((TAssertions)this); } @@ -1029,16 +1000,15 @@ public AndConstraint NotBeSameDateAs(DateTimeOffset unexpected, { DateTime unexpectedDate = unexpected.Date; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the date part of {context:the date and time} to be {0}{reason}, ", unexpectedDate) - .ForCondition(Subject.HasValue) - .FailWith("but found a DateTimeOffset.") - .Then - .ForCondition(Subject.Value.Date != unexpectedDate) - .FailWith("but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the date part of {context:the date and time} to be {0}{reason}, ", unexpectedDate, + chain => chain + .ForCondition(Subject.HasValue) + .FailWith("but found a DateTimeOffset.") + .Then + .ForCondition(Subject.Value.Date != unexpectedDate) + .FailWith("but it was.")); return new AndConstraint((TAssertions)this); } @@ -1100,7 +1070,7 @@ public AndConstraint BeOneOf(IEnumerable validValue public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(validValues.Contains(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be one of {0}{reason}, but it was {1}.", validValues, Subject); diff --git a/Src/FluentAssertions/Primitives/DateTimeOffsetRangeAssertions.cs b/Src/FluentAssertions/Primitives/DateTimeOffsetRangeAssertions.cs index deea7a999e..964b73c9db 100644 --- a/Src/FluentAssertions/Primitives/DateTimeOffsetRangeAssertions.cs +++ b/Src/FluentAssertions/Primitives/DateTimeOffsetRangeAssertions.cs @@ -23,6 +23,7 @@ public class DateTimeOffsetRangeAssertions #region Private Definitions private readonly TAssertions parentAssertions; + private readonly AssertionChain assertionChain; private readonly TimeSpanPredicate predicate; private readonly Dictionary predicates = new() @@ -39,11 +40,13 @@ public class DateTimeOffsetRangeAssertions #endregion - protected internal DateTimeOffsetRangeAssertions(TAssertions parentAssertions, DateTimeOffset? subject, + protected internal DateTimeOffsetRangeAssertions(TAssertions parentAssertions, AssertionChain assertionChain, + DateTimeOffset? subject, TimeSpanCondition condition, TimeSpan timeSpan) { this.parentAssertions = parentAssertions; + this.assertionChain = assertionChain; this.subject = subject; this.timeSpan = timeSpan; @@ -66,17 +69,17 @@ protected internal DateTimeOffsetRangeAssertions(TAssertions parentAssertions, D public AndConstraint Before(DateTimeOffset target, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be " + predicate.DisplayText + " {0} before {1}{reason}, but found a DateTime.", timeSpan, target); - if (success) + if (assertionChain.Succeeded) { TimeSpan actual = target - subject.Value; - Execute.Assertion + assertionChain .ForCondition(predicate.IsMatchedBy(actual, timeSpan)) .BecauseOf(because, becauseArgs) .FailWith( @@ -104,17 +107,17 @@ public AndConstraint Before(DateTimeOffset target, public AndConstraint After(DateTimeOffset target, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the date and time} to be " + predicate.DisplayText + " {0} after {1}{reason}, but found a DateTime.", timeSpan, target); - if (success) + if (assertionChain.Succeeded) { TimeSpan actual = subject.Value - target; - Execute.Assertion + assertionChain .ForCondition(predicate.IsMatchedBy(actual, timeSpan)) .BecauseOf(because, becauseArgs) .FailWith( diff --git a/Src/FluentAssertions/Primitives/DateTimeRangeAssertions.cs b/Src/FluentAssertions/Primitives/DateTimeRangeAssertions.cs index c6976b51bc..6192cfa16b 100644 --- a/Src/FluentAssertions/Primitives/DateTimeRangeAssertions.cs +++ b/Src/FluentAssertions/Primitives/DateTimeRangeAssertions.cs @@ -23,6 +23,7 @@ public class DateTimeRangeAssertions #region Private Definitions private readonly TAssertions parentAssertions; + private readonly AssertionChain assertionChain; private readonly TimeSpanPredicate predicate; private readonly Dictionary predicates = new() @@ -39,11 +40,13 @@ public class DateTimeRangeAssertions #endregion - protected internal DateTimeRangeAssertions(TAssertions parentAssertions, DateTime? subject, + protected internal DateTimeRangeAssertions(TAssertions parentAssertions, AssertionChain assertionChain, + DateTime? subject, TimeSpanCondition condition, TimeSpan timeSpan) { this.parentAssertions = parentAssertions; + this.assertionChain = assertionChain; this.subject = subject; this.timeSpan = timeSpan; @@ -66,18 +69,18 @@ protected internal DateTimeRangeAssertions(TAssertions parentAssertions, DateTim public AndConstraint Before(DateTime target, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected date and/or time {0} to be " + predicate.DisplayText + " {1} before {2}{reason}, but found a DateTime.", subject, timeSpan, target); - if (success) + if (assertionChain.Succeeded) { TimeSpan actual = target - subject.Value; - Execute.Assertion + assertionChain .ForCondition(predicate.IsMatchedBy(actual, timeSpan)) .BecauseOf(because, becauseArgs) .FailWith( @@ -105,18 +108,18 @@ public AndConstraint Before(DateTime target, public AndConstraint After(DateTime target, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected date and/or time {0} to be " + predicate.DisplayText + " {1} after {2}{reason}, but found a DateTime.", subject, timeSpan, target); - if (success) + if (assertionChain.Succeeded) { TimeSpan actual = subject.Value - target; - Execute.Assertion + assertionChain .ForCondition(predicate.IsMatchedBy(actual, timeSpan)) .BecauseOf(because, becauseArgs) .FailWith( diff --git a/Src/FluentAssertions/Primitives/EnumAssertions.cs b/Src/FluentAssertions/Primitives/EnumAssertions.cs index 69d8b110ef..b740b3c7d3 100644 --- a/Src/FluentAssertions/Primitives/EnumAssertions.cs +++ b/Src/FluentAssertions/Primitives/EnumAssertions.cs @@ -15,8 +15,8 @@ namespace FluentAssertions.Primitives; public class EnumAssertions : EnumAssertions> where TEnum : struct, Enum { - public EnumAssertions(TEnum subject) - : base(subject) + public EnumAssertions(TEnum subject, AssertionChain assertionChain) + : base(subject, assertionChain) { } } @@ -30,13 +30,16 @@ public class EnumAssertions where TEnum : struct, Enum where TAssertions : EnumAssertions { - public EnumAssertions(TEnum subject) - : this((TEnum?)subject) + private readonly AssertionChain assertionChain; + + public EnumAssertions(TEnum subject, AssertionChain assertionChain) + : this((TEnum?)subject, assertionChain) { } - private protected EnumAssertions(TEnum? value) + private protected EnumAssertions(TEnum? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -56,7 +59,7 @@ private protected EnumAssertions(TEnum? value) public AndConstraint Be(TEnum expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject?.Equals(expected) == true) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to be {0}{reason}, but found {1}.", @@ -79,7 +82,7 @@ public AndConstraint Be(TEnum expected, public AndConstraint Be(TEnum? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Nullable.Equals(Subject, expected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to be {0}{reason}, but found {1}.", @@ -102,7 +105,7 @@ public AndConstraint Be(TEnum? expected, public AndConstraint NotBe(TEnum unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject?.Equals(unexpected) != true) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} not to be {0}{reason}, but it is.", unexpected); @@ -124,7 +127,7 @@ public AndConstraint NotBe(TEnum unexpected, public AndConstraint NotBe(TEnum? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Nullable.Equals(Subject, unexpected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} not to be {0}{reason}, but it is.", unexpected); @@ -142,18 +145,17 @@ public AndConstraint NotBe(TEnum? unexpected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint BeDefined([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeDefined([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:the enum} to be defined in {0}{reason}, ", typeof(TEnum)) - .ForCondition(Subject is not null) - .FailWith("but found .") - .Then - .ForCondition(Enum.IsDefined(typeof(TEnum), Subject)) - .FailWith("but it is not.") - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:the enum} to be defined in {0}{reason}, ", typeof(TEnum), chain => chain + .ForCondition(Subject is not null) + .FailWith("but found .") + .Then + .ForCondition(Enum.IsDefined(typeof(TEnum), Subject)) + .FailWith("but it is not.")); return new AndConstraint((TAssertions)this); } @@ -168,18 +170,17 @@ public AndConstraint BeDefined([StringSyntax("CompositeFormat")] st /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeDefined([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeDefined([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect {context:the enum} to be defined in {0}{reason}, ", typeof(TEnum)) - .ForCondition(Subject is not null) - .FailWith("but found .") - .Then - .ForCondition(!Enum.IsDefined(typeof(TEnum), Subject)) - .FailWith("but it is.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect {context:the enum} to be defined in {0}{reason}, ", typeof(TEnum), chain => chain + .ForCondition(Subject is not null) + .FailWith("but found .") + .Then + .ForCondition(!Enum.IsDefined(typeof(TEnum), Subject)) + .FailWith("but it is.")); return new AndConstraint((TAssertions)this); } @@ -198,7 +199,7 @@ public AndConstraint NotBeDefined([StringSyntax("CompositeFormat")] public AndConstraint HaveValue(decimal expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is { } value && GetValue(value) == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to have value {0}{reason}, but found {1}.", @@ -221,7 +222,7 @@ public AndConstraint HaveValue(decimal expected, public AndConstraint NotHaveValue(decimal unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!(Subject is { } value && GetValue(value) == unexpected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to not have value {0}{reason}, but found {1}.", @@ -245,7 +246,7 @@ public AndConstraint HaveSameValueAs(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where T : struct, Enum { - Execute.Assertion + assertionChain .ForCondition(Subject is { } value && GetValue(value) == GetValue(expected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to have same value as {0}{reason}, but found {1}.", @@ -269,7 +270,7 @@ public AndConstraint NotHaveSameValueAs(T unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where T : struct, Enum { - Execute.Assertion + assertionChain .ForCondition(!(Subject is { } value && GetValue(value) == GetValue(unexpected))) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to not have same value as {0}{reason}, but found {1}.", @@ -293,7 +294,7 @@ public AndConstraint HaveSameNameAs(T expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where T : struct, Enum { - Execute.Assertion + assertionChain .ForCondition(Subject is { } value && GetName(value) == GetName(expected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to have same name as {0}{reason}, but found {1}.", @@ -317,7 +318,7 @@ public AndConstraint NotHaveSameNameAs(T unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where T : struct, Enum { - Execute.Assertion + assertionChain .ForCondition(!(Subject is { } value && GetName(value) == GetName(unexpected))) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to not have same name as {0}{reason}, but found {1}.", @@ -340,7 +341,7 @@ public AndConstraint NotHaveSameNameAs(T unexpected, public AndConstraint HaveFlag(TEnum expectedFlag, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject?.HasFlag(expectedFlag) == true) .FailWith("Expected {context:the enum} to have flag {0}{reason}, but found {1}.", expectedFlag, Subject); @@ -362,7 +363,7 @@ public AndConstraint HaveFlag(TEnum expectedFlag, public AndConstraint NotHaveFlag(TEnum unexpectedFlag, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject?.HasFlag(unexpectedFlag) != true) .FailWith("Expected {context:the enum} to not have flag {0}{reason}.", unexpectedFlag); @@ -389,7 +390,7 @@ public AndConstraint Match(Expression> predicate { Guard.ThrowIfArgumentIsNull(predicate, nameof(predicate), "Cannot match an enum against a predicate."); - Execute.Assertion + assertionChain .ForCondition(predicate.Compile()(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:the enum} to match {1}{reason}, but found {0}.", Subject, predicate.Body); @@ -432,7 +433,7 @@ public AndConstraint BeOneOf(IEnumerable validValues, Guard.ThrowIfArgumentIsEmpty(validValues, nameof(validValues), "Cannot assert that an enum is one of an empty list of enums"); - Execute.Assertion + assertionChain .ForCondition(Subject is not null) .FailWith("Expected {context:the enum} to be one of {0}{reason}, but found ", validValues) .Then diff --git a/Src/FluentAssertions/Primitives/GuidAssertions.cs b/Src/FluentAssertions/Primitives/GuidAssertions.cs index 648faa7964..d30fe808df 100644 --- a/Src/FluentAssertions/Primitives/GuidAssertions.cs +++ b/Src/FluentAssertions/Primitives/GuidAssertions.cs @@ -11,8 +11,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class GuidAssertions : GuidAssertions { - public GuidAssertions(Guid? value) - : base(value) + public GuidAssertions(Guid? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -26,8 +26,11 @@ public GuidAssertions(Guid? value) public class GuidAssertions where TAssertions : GuidAssertions { - public GuidAssertions(Guid? value) + private readonly AssertionChain assertionChain; + + public GuidAssertions(Guid? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -50,7 +53,7 @@ public GuidAssertions(Guid? value) /// public AndConstraint BeEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == Guid.Empty) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:Guid} to be empty{reason}, but found {0}.", Subject); @@ -70,7 +73,7 @@ public AndConstraint BeEmpty([StringSyntax("CompositeFormat")] stri /// public AndConstraint NotBeEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is { } value && value != Guid.Empty) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:Guid} to be empty{reason}."); @@ -119,7 +122,7 @@ public AndConstraint Be(string expected, public AndConstraint Be(Guid expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:Guid} to be {0}{reason}, but found {1}.", expected, Subject); @@ -164,7 +167,7 @@ public AndConstraint NotBe(string unexpected, public AndConstraint NotBe(Guid unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:Guid} to be {0}{reason}.", Subject); diff --git a/Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs b/Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs index f688e7a32f..e483ac43b7 100644 --- a/Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs +++ b/Src/FluentAssertions/Primitives/HttpResponseMessageAssertions.cs @@ -12,8 +12,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class HttpResponseMessageAssertions : HttpResponseMessageAssertions { - public HttpResponseMessageAssertions(HttpResponseMessage value) - : base(value) + public HttpResponseMessageAssertions(HttpResponseMessage value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -25,9 +25,12 @@ public HttpResponseMessageAssertions(HttpResponseMessage value) public class HttpResponseMessageAssertions : ObjectAssertions where TAssertions : HttpResponseMessageAssertions { - protected HttpResponseMessageAssertions(HttpResponseMessage value) - : base(value) + private readonly AssertionChain assertionChain; + + protected HttpResponseMessageAssertions(HttpResponseMessage value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -42,14 +45,14 @@ protected HttpResponseMessageAssertions(HttpResponseMessage value) /// public AndConstraint BeSuccessful([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be successful (2xx){reason}, but HttpResponseMessage was ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject!.IsSuccessStatusCode) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be successful (2xx){reason}, but found {0}.", Subject.StatusCode); @@ -70,14 +73,14 @@ public AndConstraint BeSuccessful([StringSyntax("CompositeFormat")] /// public AndConstraint BeRedirection([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be redirection (3xx){reason}, but HttpResponseMessage was ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition((int)Subject!.StatusCode is >= 300 and <= 399) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be redirection (3xx){reason}, but found {0}.", Subject.StatusCode); @@ -98,14 +101,14 @@ public AndConstraint BeRedirection([StringSyntax("CompositeFormat") /// public AndConstraint HaveError([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be an error{reason}, but HttpResponseMessage was ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(IsClientError() || IsServerError()) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be an error{reason}, but found {0}.", Subject.StatusCode); @@ -126,14 +129,14 @@ public AndConstraint HaveError([StringSyntax("CompositeFormat")] st /// public AndConstraint HaveClientError([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be client error (4xx){reason}, but HttpResponseMessage was ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(IsClientError()) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be client error (4xx){reason}, but found {0}.", Subject.StatusCode); @@ -154,14 +157,14 @@ public AndConstraint HaveClientError([StringSyntax("CompositeFormat /// public AndConstraint HaveServerError([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be server error (5xx){reason}, but HttpResponseMessage was ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(IsServerError()) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be server error (5xx){reason}, but found {0}.", Subject.StatusCode); @@ -184,14 +187,14 @@ public AndConstraint HaveServerError([StringSyntax("CompositeFormat public AndConstraint HaveStatusCode(HttpStatusCode expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be {0}{reason}, but HttpResponseMessage was .", expected); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject!.StatusCode == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode to be {0}{reason}, but found {1}.", expected, Subject.StatusCode); @@ -214,14 +217,14 @@ public AndConstraint HaveStatusCode(HttpStatusCode expected, public AndConstraint NotHaveStatusCode(HttpStatusCode unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode not to be {0}{reason}, but HttpResponseMessage was .", unexpected); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject!.StatusCode != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected HttpStatusCode not to be {0}{reason}, but found {1}.", unexpected, Subject.StatusCode); diff --git a/Src/FluentAssertions/Primitives/IStringComparisonStrategy.cs b/Src/FluentAssertions/Primitives/IStringComparisonStrategy.cs index da95c7ccbb..6dffb8b1f4 100644 --- a/Src/FluentAssertions/Primitives/IStringComparisonStrategy.cs +++ b/Src/FluentAssertions/Primitives/IStringComparisonStrategy.cs @@ -15,5 +15,5 @@ internal interface IStringComparisonStrategy /// /// Asserts that the matches the value. /// - void ValidateAgainstMismatch(IAssertionScope assertion, string subject, string expected); + void ValidateAgainstMismatch(AssertionChain assertionChain, string subject, string expected); } diff --git a/Src/FluentAssertions/Primitives/NullableBooleanAssertions.cs b/Src/FluentAssertions/Primitives/NullableBooleanAssertions.cs index 6b5954d946..0a856363fa 100644 --- a/Src/FluentAssertions/Primitives/NullableBooleanAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableBooleanAssertions.cs @@ -10,8 +10,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class NullableBooleanAssertions : NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) - : base(value) + public NullableBooleanAssertions(bool? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -23,9 +23,12 @@ public NullableBooleanAssertions(bool? value) public class NullableBooleanAssertions : BooleanAssertions where TAssertions : NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) - : base(value) + private readonly AssertionChain assertionChain; + + public NullableBooleanAssertions(bool? value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -40,7 +43,7 @@ public NullableBooleanAssertions(bool? value) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected a value{reason}."); @@ -75,7 +78,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect a value{reason}, but found {0}.", Subject); @@ -112,7 +115,7 @@ public AndConstraint BeNull([StringSyntax("CompositeFormat")] strin public AndConstraint Be(bool? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {0}{reason}, but found {1}.", expected, Subject); @@ -134,7 +137,7 @@ public AndConstraint Be(bool? expected, public AndConstraint NotBe(bool? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:nullable boolean} not to be {0}{reason}, but found {1}.", unexpected, Subject); @@ -154,7 +157,7 @@ public AndConstraint NotBe(bool? unexpected, /// public AndConstraint NotBeFalse([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is not false) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:nullable boolean} not to be {0}{reason}, but found {1}.", false, Subject); @@ -174,7 +177,7 @@ public AndConstraint NotBeFalse([StringSyntax("CompositeFormat")] s /// public AndConstraint NotBeTrue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is not true) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:nullable boolean} not to be {0}{reason}, but found {1}.", true, Subject); diff --git a/Src/FluentAssertions/Primitives/NullableDateOnlyAssertions.cs b/Src/FluentAssertions/Primitives/NullableDateOnlyAssertions.cs index b4733fbf01..ecdde62a34 100644 --- a/Src/FluentAssertions/Primitives/NullableDateOnlyAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableDateOnlyAssertions.cs @@ -13,8 +13,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class NullableDateOnlyAssertions : NullableDateOnlyAssertions { - public NullableDateOnlyAssertions(DateOnly? value) - : base(value) + public NullableDateOnlyAssertions(DateOnly? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -26,9 +26,12 @@ public NullableDateOnlyAssertions(DateOnly? value) public class NullableDateOnlyAssertions : DateOnlyAssertions where TAssertions : NullableDateOnlyAssertions { - public NullableDateOnlyAssertions(DateOnly? value) - : base(value) + private readonly AssertionChain assertionChain; + + public NullableDateOnlyAssertions(DateOnly? value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -43,7 +46,7 @@ public NullableDateOnlyAssertions(DateOnly? value) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:nullable date} to have a value{reason}, but found {0}.", Subject); @@ -78,7 +81,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:nullable date} to have a value{reason}, but found {0}.", Subject); diff --git a/Src/FluentAssertions/Primitives/NullableDateTimeAssertions.cs b/Src/FluentAssertions/Primitives/NullableDateTimeAssertions.cs index 2f897eeaa0..d7b8f1bc13 100644 --- a/Src/FluentAssertions/Primitives/NullableDateTimeAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableDateTimeAssertions.cs @@ -15,8 +15,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class NullableDateTimeAssertions : NullableDateTimeAssertions { - public NullableDateTimeAssertions(DateTime? expected) - : base(expected) + public NullableDateTimeAssertions(DateTime? expected, AssertionChain assertionChain) + : base(expected, assertionChain) { } } @@ -31,9 +31,12 @@ public NullableDateTimeAssertions(DateTime? expected) public class NullableDateTimeAssertions : DateTimeAssertions where TAssertions : NullableDateTimeAssertions { - public NullableDateTimeAssertions(DateTime? expected) - : base(expected) + private readonly AssertionChain assertionChain; + + public NullableDateTimeAssertions(DateTime? expected, AssertionChain assertionChain) + : base(expected, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -48,7 +51,7 @@ public NullableDateTimeAssertions(DateTime? expected) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:nullable date and time} to have a value{reason}, but found {0}.", Subject); @@ -83,7 +86,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:nullable date and time} to have a value{reason}, but found {0}.", Subject); diff --git a/Src/FluentAssertions/Primitives/NullableDateTimeOffsetAssertions.cs b/Src/FluentAssertions/Primitives/NullableDateTimeOffsetAssertions.cs index 48553d4ada..3508fb2f36 100644 --- a/Src/FluentAssertions/Primitives/NullableDateTimeOffsetAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableDateTimeOffsetAssertions.cs @@ -15,8 +15,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class NullableDateTimeOffsetAssertions : NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(DateTimeOffset? expected) - : base(expected) + public NullableDateTimeOffsetAssertions(DateTimeOffset? expected, AssertionChain assertionChain) + : base(expected, assertionChain) { } } @@ -32,9 +32,12 @@ public NullableDateTimeOffsetAssertions(DateTimeOffset? expected) public class NullableDateTimeOffsetAssertions : DateTimeOffsetAssertions where TAssertions : NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(DateTimeOffset? expected) - : base(expected) + private readonly AssertionChain assertionChain; + + public NullableDateTimeOffsetAssertions(DateTimeOffset? expected, AssertionChain assertionChain) + : base(expected, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -49,7 +52,7 @@ public NullableDateTimeOffsetAssertions(DateTimeOffset? expected) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:variable} to have a value{reason}, but found {0}", Subject); @@ -85,7 +88,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:variable} to have a value{reason}, but found {0}", Subject); diff --git a/Src/FluentAssertions/Primitives/NullableEnumAssertions.cs b/Src/FluentAssertions/Primitives/NullableEnumAssertions.cs index 85f2ce95e6..7096c1ab53 100644 --- a/Src/FluentAssertions/Primitives/NullableEnumAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableEnumAssertions.cs @@ -10,8 +10,8 @@ namespace FluentAssertions.Primitives; public class NullableEnumAssertions : NullableEnumAssertions> where TEnum : struct, Enum { - public NullableEnumAssertions(TEnum? subject) - : base(subject) + public NullableEnumAssertions(TEnum? subject, AssertionChain assertionChain) + : base(subject, assertionChain) { } } @@ -23,9 +23,12 @@ public class NullableEnumAssertions : EnumAssertions { - public NullableEnumAssertions(TEnum? subject) - : base(subject) + private readonly AssertionChain assertionChain; + + public NullableEnumAssertions(TEnum? subject, AssertionChain assertionChain) + : base(subject, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -40,12 +43,12 @@ public NullableEnumAssertions(TEnum? subject) /// public AndWhichConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:nullable enum} to have a value{reason}, but found {0}.", Subject); - return new AndWhichConstraint((TAssertions)this, Subject.GetValueOrDefault()); + return new AndWhichConstraint((TAssertions)this, Subject.GetValueOrDefault(), assertionChain, ".Value"); } /// @@ -75,7 +78,7 @@ public AndWhichConstraint NotBeNull([StringSyntax("Composite /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:nullable enum} to have a value{reason}, but found {0}.", Subject); diff --git a/Src/FluentAssertions/Primitives/NullableGuidAssertions.cs b/Src/FluentAssertions/Primitives/NullableGuidAssertions.cs index 45c26f7915..1eea1e758b 100644 --- a/Src/FluentAssertions/Primitives/NullableGuidAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableGuidAssertions.cs @@ -11,8 +11,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class NullableGuidAssertions : NullableGuidAssertions { - public NullableGuidAssertions(Guid? value) - : base(value) + public NullableGuidAssertions(Guid? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -24,9 +24,12 @@ public NullableGuidAssertions(Guid? value) public class NullableGuidAssertions : GuidAssertions where TAssertions : NullableGuidAssertions { - public NullableGuidAssertions(Guid? value) - : base(value) + private readonly AssertionChain assertionChain; + + public NullableGuidAssertions(Guid? value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -41,7 +44,7 @@ public NullableGuidAssertions(Guid? value) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected a value{reason}."); @@ -76,7 +79,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect a value{reason}, but found {0}.", Subject); @@ -112,7 +115,7 @@ public AndConstraint BeNull([StringSyntax("CompositeFormat")] strin /// public AndConstraint Be(Guid? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:Guid} to be {0}{reason}, but found {1}.", expected, Subject); diff --git a/Src/FluentAssertions/Primitives/NullableSimpleTimeSpanAssertions.cs b/Src/FluentAssertions/Primitives/NullableSimpleTimeSpanAssertions.cs index da52304bd1..03ffb3d9bd 100644 --- a/Src/FluentAssertions/Primitives/NullableSimpleTimeSpanAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableSimpleTimeSpanAssertions.cs @@ -15,8 +15,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class NullableSimpleTimeSpanAssertions : NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(TimeSpan? value) - : base(value) + public NullableSimpleTimeSpanAssertions(TimeSpan? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -32,9 +32,12 @@ public NullableSimpleTimeSpanAssertions(TimeSpan? value) public class NullableSimpleTimeSpanAssertions : SimpleTimeSpanAssertions where TAssertions : NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(TimeSpan? value) - : base(value) + private readonly AssertionChain assertionChain; + + public NullableSimpleTimeSpanAssertions(TimeSpan? value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -49,7 +52,7 @@ public NullableSimpleTimeSpanAssertions(TimeSpan? value) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected a value{reason}."); @@ -84,7 +87,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect a value{reason}, but found {0}.", Subject); @@ -121,7 +124,7 @@ public AndConstraint BeNull([StringSyntax("CompositeFormat")] strin public AndConstraint Be(TimeSpan? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {0}{reason}, but found {1}.", expected, Subject); diff --git a/Src/FluentAssertions/Primitives/NullableTimeOnlyAssertions.cs b/Src/FluentAssertions/Primitives/NullableTimeOnlyAssertions.cs index d0235efee0..8e6ede5140 100644 --- a/Src/FluentAssertions/Primitives/NullableTimeOnlyAssertions.cs +++ b/Src/FluentAssertions/Primitives/NullableTimeOnlyAssertions.cs @@ -13,8 +13,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class NullableTimeOnlyAssertions : NullableTimeOnlyAssertions { - public NullableTimeOnlyAssertions(TimeOnly? value) - : base(value) + public NullableTimeOnlyAssertions(TimeOnly? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -26,9 +26,12 @@ public NullableTimeOnlyAssertions(TimeOnly? value) public class NullableTimeOnlyAssertions : TimeOnlyAssertions where TAssertions : NullableTimeOnlyAssertions { - public NullableTimeOnlyAssertions(TimeOnly? value) - : base(value) + private readonly AssertionChain assertionChain; + + public NullableTimeOnlyAssertions(TimeOnly? value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -43,7 +46,7 @@ public NullableTimeOnlyAssertions(TimeOnly? value) /// public AndConstraint HaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:nullable time} to have a value{reason}, but found {0}.", Subject); @@ -78,7 +81,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st /// public AndConstraint NotHaveValue([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!Subject.HasValue) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:nullable time} to have a value{reason}, but found {0}.", Subject); diff --git a/Src/FluentAssertions/Primitives/ObjectAssertions.cs b/Src/FluentAssertions/Primitives/ObjectAssertions.cs index d6bdccfab0..9c536c3d66 100644 --- a/Src/FluentAssertions/Primitives/ObjectAssertions.cs +++ b/Src/FluentAssertions/Primitives/ObjectAssertions.cs @@ -13,9 +13,12 @@ namespace FluentAssertions.Primitives; /// public class ObjectAssertions : ObjectAssertions { - public ObjectAssertions(object value) - : base(value) + private readonly AssertionChain assertionChain; + + public ObjectAssertions(object value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -38,7 +41,7 @@ public AndConstraint Be(TExpectation expected, I { Guard.ThrowIfArgumentIsNull(comparer); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is TExpectation subject && comparer.Equals(subject, expected)) .WithDefaultIdentifier(Identifier) @@ -68,7 +71,7 @@ public AndConstraint NotBe(TExpectation unexpect { Guard.ThrowIfArgumentIsNull(comparer); - Execute.Assertion + assertionChain .ForCondition(Subject is not TExpectation subject || !comparer.Equals(subject, unexpected)) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -102,7 +105,7 @@ public AndConstraint BeOneOf(IEnumerable BeOneOf(IEnumerable : ReferenceTypeAssertions where TAssertions : ObjectAssertions { - public ObjectAssertions(TSubject value) - : base(value) + private readonly AssertionChain assertionChain; + + public ObjectAssertions(TSubject value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -138,12 +144,11 @@ public ObjectAssertions(TSubject value) public AndConstraint Be(TSubject expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(ObjectExtensions.GetComparer()(Subject, expected)) .WithDefaultIdentifier(Identifier) - .FailWith("Expected {context} to be {0}{reason}, but found {1}.", expected, - Subject); + .FailWith("Expected {context} to be {0}{reason}, but found {1}.", expected, Subject); return new AndConstraint((TAssertions)this); } @@ -168,7 +173,7 @@ public AndConstraint Be(TSubject expected, IEqualityComparer Be(TSubject expected, IEqualityComparer NotBe(TSubject unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!ObjectExtensions.GetComparer()(Subject, unexpected)) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -221,7 +226,7 @@ public AndConstraint NotBe(TSubject unexpected, IEqualityComparer BeEquivalentTo(TExpectation expe EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); var context = new EquivalencyValidationContext(Node.From(() => - AssertionScope.Current.CallerIdentity), options) + CurrentAssertionChain.CallerIdentifier), options) { Reason = new Reason(because, becauseArgs), TraceWriter = options.TraceWriter @@ -369,7 +374,7 @@ public AndConstraint NotBeEquivalentTo( hasMismatches = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(hasMismatches) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -405,7 +410,7 @@ public AndConstraint BeOneOf(params TSubject[] validValues) public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(validValues.Contains(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be one of {0}{reason}, but found {1}.", validValues, Subject); @@ -438,7 +443,7 @@ public AndConstraint BeOneOf(IEnumerable validValues, Guard.ThrowIfArgumentIsNull(validValues); Guard.ThrowIfArgumentIsNull(comparer); - Execute.Assertion + assertionChain .ForCondition(validValues.Contains(Subject, comparer)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:object} to be one of {0}{reason}, but found {1}.", validValues, Subject); diff --git a/Src/FluentAssertions/Primitives/ReferenceTypeAssertions.cs b/Src/FluentAssertions/Primitives/ReferenceTypeAssertions.cs index 8817613104..9a08c161f7 100644 --- a/Src/FluentAssertions/Primitives/ReferenceTypeAssertions.cs +++ b/Src/FluentAssertions/Primitives/ReferenceTypeAssertions.cs @@ -17,8 +17,9 @@ namespace FluentAssertions.Primitives; public abstract class ReferenceTypeAssertions where TAssertions : ReferenceTypeAssertions { - protected ReferenceTypeAssertions(TSubject subject) + protected ReferenceTypeAssertions(TSubject subject, AssertionChain assertionChain) { + CurrentAssertionChain = assertionChain; Subject = subject; } @@ -39,7 +40,7 @@ protected ReferenceTypeAssertions(TSubject subject) /// public AndConstraint BeNull([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is null) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -58,9 +59,10 @@ public AndConstraint BeNull([StringSyntax("CompositeFormat")] strin /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -83,7 +85,7 @@ public AndConstraint NotBeNull([StringSyntax("CompositeFormat")] st public AndConstraint BeSameAs(TSubject expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(ReferenceEquals(Subject, expected)) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -106,7 +108,7 @@ public AndConstraint BeSameAs(TSubject expected, public AndConstraint NotBeSameAs(TSubject unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + CurrentAssertionChain .ForCondition(!ReferenceEquals(Subject, unexpected)) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -126,7 +128,8 @@ public AndConstraint NotBeSameAs(TSubject unexpected, /// /// Zero or more objects to format using the placeholders in . /// - public AndWhichConstraint BeOfType([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndWhichConstraint BeOfType([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { BeOfType(typeof(T), because, becauseArgs); @@ -156,13 +159,13 @@ public AndConstraint BeOfType(Type expectedType, { Guard.ThrowIfArgumentIsNull(expectedType); - bool success = Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier("type") .FailWith("Expected {context} to be {0}{reason}, but found .", expectedType); - if (success) + if (CurrentAssertionChain.Succeeded) { Type subjectType = Subject.GetType(); @@ -190,7 +193,8 @@ public AndConstraint BeOfType(Type expectedType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeOfType([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeOfType([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { NotBeOfType(typeof(T), because, becauseArgs); @@ -216,13 +220,13 @@ public AndConstraint NotBeOfType(Type unexpectedType, { Guard.ThrowIfArgumentIsNull(unexpectedType); - bool success = Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier("type") .FailWith("Expected {context} not to be {0}{reason}, but found .", unexpectedType); - if (success) + if (CurrentAssertionChain.Succeeded) { Type subjectType = Subject.GetType(); @@ -250,18 +254,19 @@ public AndConstraint NotBeOfType(Type unexpectedType, /// /// Zero or more objects to format using the placeholders in . /// - /// An which can be used to chain assertions. - public AndWhichConstraint BeAssignableTo([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + /// An which can be used to chain assertions. + public AndWhichConstraint BeAssignableTo([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier("type") .FailWith("Expected {context} to be assignable to {0}{reason}, but found .", typeof(T)); - if (success) + if (CurrentAssertionChain.Succeeded) { - Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is T) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -293,19 +298,19 @@ public AndConstraint BeAssignableTo(Type type, { Guard.ThrowIfArgumentIsNull(type); - bool success = Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier("type") .FailWith("Expected {context} to be assignable to {0}{reason}, but found .", type); - if (success) + if (CurrentAssertionChain.Succeeded) { bool isAssignable = type.IsGenericTypeDefinition ? Subject.GetType().IsAssignableToOpenGeneric(type) : type.IsAssignableFrom(Subject.GetType()); - Execute.Assertion + CurrentAssertionChain .ForCondition(isAssignable) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -329,7 +334,8 @@ public AndConstraint BeAssignableTo(Type type, /// Zero or more objects to format using the placeholders in . /// /// An which can be used to chain assertions. - public AndConstraint NotBeAssignableTo([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeAssignableTo([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotBeAssignableTo(typeof(T), because, becauseArgs); } @@ -352,19 +358,19 @@ public AndConstraint NotBeAssignableTo(Type type, { Guard.ThrowIfArgumentIsNull(type); - bool success = Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier("type") .FailWith("Expected {context} to not be assignable to {0}{reason}, but found .", type); - if (success) + if (CurrentAssertionChain.Succeeded) { bool isAssignable = type.IsGenericTypeDefinition ? Subject.GetType().IsAssignableToOpenGeneric(type) : type.IsAssignableFrom(Subject.GetType()); - Execute.Assertion + CurrentAssertionChain .ForCondition(!isAssignable) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -411,7 +417,7 @@ public AndConstraint Match(Expression> predicate, { Guard.ThrowIfArgumentIsNull(predicate, nameof(predicate), "Cannot match an object against a predicate."); - Execute.Assertion + CurrentAssertionChain .ForCondition(predicate.Compile()((T)Subject)) .BecauseOf(because, becauseArgs) .WithDefaultIdentifier(Identifier) @@ -434,16 +440,17 @@ public AndConstraint Satisfy(Action assertion) { Guard.ThrowIfArgumentIsNull(assertion, nameof(assertion), "Cannot verify an object against a inspector."); - var success = Execute.Assertion + CurrentAssertionChain .ForCondition(Subject is not null) .WithDefaultIdentifier(Identifier) .FailWith("Expected {context:object} to be assignable to {0}{reason}, but found .", typeof(T)) .Then .ForCondition(Subject is T) .WithDefaultIdentifier(Identifier) - .FailWith("Expected {context:object} to be assignable to {0}{reason}, but {1} is not.", typeof(T), Subject?.GetType()); + .FailWith("Expected {context:object} to be assignable to {0}{reason}, but {1} is not.", typeof(T), + Subject?.GetType()); - if (success) + if (CurrentAssertionChain.Succeeded) { string[] failuresFromInspector; @@ -458,10 +465,11 @@ public AndConstraint Satisfy(Action assertion) string failureMessage = Environment.NewLine + string.Join(Environment.NewLine, failuresFromInspector.Select(x => x.IndentLines())); - Execute.Assertion + CurrentAssertionChain .WithDefaultIdentifier(Identifier) - .WithExpectation("Expected {context:object} to match inspector, but the inspector was not satisfied:", Subject) - .FailWithPreFormatted(failureMessage); + .WithExpectation("Expected {context:object} to match inspector, but the inspector was not satisfied:", + Subject, + chain => chain.FailWithPreFormatted(failureMessage)); } } @@ -477,4 +485,6 @@ public AndConstraint Satisfy(Action assertion) /// public override bool Equals(object obj) => throw new NotSupportedException("Equals is not part of Fluent Assertions. Did you mean BeSameAs() instead?"); + + public AssertionChain CurrentAssertionChain { get; } } diff --git a/Src/FluentAssertions/Primitives/SimpleTimeSpanAssertions.cs b/Src/FluentAssertions/Primitives/SimpleTimeSpanAssertions.cs index 7387544b22..634feee5a7 100644 --- a/Src/FluentAssertions/Primitives/SimpleTimeSpanAssertions.cs +++ b/Src/FluentAssertions/Primitives/SimpleTimeSpanAssertions.cs @@ -12,8 +12,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class SimpleTimeSpanAssertions : SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(TimeSpan? value) - : base(value) + public SimpleTimeSpanAssertions(TimeSpan? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -27,8 +27,11 @@ public SimpleTimeSpanAssertions(TimeSpan? value) public class SimpleTimeSpanAssertions where TAssertions : SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(TimeSpan? value) + private readonly AssertionChain assertionChain; + + public SimpleTimeSpanAssertions(TimeSpan? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -49,7 +52,7 @@ public SimpleTimeSpanAssertions(TimeSpan? value) /// public AndConstraint BePositive([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject > TimeSpan.Zero) .FailWith("Expected {context:time} to be positive{reason}, but found {0}.", Subject); @@ -69,7 +72,7 @@ public AndConstraint BePositive([StringSyntax("CompositeFormat")] s /// public AndConstraint BeNegative([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject < TimeSpan.Zero) .FailWith("Expected {context:time} to be negative{reason}, but found {0}.", Subject); @@ -92,7 +95,7 @@ public AndConstraint BeNegative([StringSyntax("CompositeFormat")] s public AndConstraint Be(TimeSpan expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(expected == Subject) .FailWith("Expected {0}{reason}, but found {1}.", expected, Subject); @@ -115,7 +118,7 @@ public AndConstraint Be(TimeSpan expected, public AndConstraint NotBe(TimeSpan unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(unexpected != Subject) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {0}{reason}.", unexpected); @@ -138,7 +141,7 @@ public AndConstraint NotBe(TimeSpan unexpected, public AndConstraint BeLessThan(TimeSpan expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject < expected) .FailWith("Expected {context:time} to be less than {0}{reason}, but found {1}.", expected, Subject); @@ -161,7 +164,7 @@ public AndConstraint BeLessThan(TimeSpan expected, public AndConstraint BeLessThanOrEqualTo(TimeSpan expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject <= expected) .FailWith("Expected {context:time} to be less than or equal to {0}{reason}, but found {1}.", expected, Subject); @@ -184,7 +187,7 @@ public AndConstraint BeLessThanOrEqualTo(TimeSpan expected, public AndConstraint BeGreaterThan(TimeSpan expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject > expected) .FailWith("Expected {context:time} to be greater than {0}{reason}, but found {1}.", expected, Subject); @@ -207,7 +210,7 @@ public AndConstraint BeGreaterThan(TimeSpan expected, public AndConstraint BeGreaterThanOrEqualTo(TimeSpan expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject >= expected) .FailWith("Expected {context:time} to be greater than or equal to {0}{reason}, but found {1}.", expected, Subject); @@ -245,7 +248,7 @@ public AndConstraint BeCloseTo(TimeSpan nearbyTime, TimeSpan precis TimeSpan minimumValue = nearbyTime - precision; TimeSpan maximumValue = nearbyTime + precision; - Execute.Assertion + assertionChain .ForCondition(Subject >= minimumValue && Subject.Value <= maximumValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be within {0} from {1}{reason}, but found {2}.", @@ -285,7 +288,7 @@ public AndConstraint NotBeCloseTo(TimeSpan distantTime, TimeSpan pr TimeSpan minimumValue = distantTime - precision; TimeSpan maximumValue = distantTime + precision; - Execute.Assertion + assertionChain .ForCondition(Subject < minimumValue || Subject > maximumValue) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to not be within {0} from {1}{reason}, but found {2}.", diff --git a/Src/FluentAssertions/Primitives/StringAssertions.cs b/Src/FluentAssertions/Primitives/StringAssertions.cs index dbc6a5e7f2..7e1fac4730 100644 --- a/Src/FluentAssertions/Primitives/StringAssertions.cs +++ b/Src/FluentAssertions/Primitives/StringAssertions.cs @@ -20,8 +20,8 @@ public class StringAssertions : StringAssertions /// /// Initializes a new instance of the class. /// - public StringAssertions(string value) - : base(value) + public StringAssertions(string value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -33,12 +33,15 @@ public StringAssertions(string value) public class StringAssertions : ReferenceTypeAssertions where TAssertions : StringAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public StringAssertions(string value) - : base(value) + public StringAssertions(string value, AssertionChain assertionChain) + : base(value, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -55,7 +58,7 @@ public StringAssertions(string value) public AndConstraint Be(string expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var stringEqualityValidator = new StringValidator( + var stringEqualityValidator = new StringValidator(assertionChain, new StringEqualityStrategy(StringComparer.Ordinal, "be"), because, becauseArgs); @@ -91,7 +94,7 @@ public AndConstraint BeOneOf(params string[] validValues) public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(validValues.Contains(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} to be one of {0}{reason}, but found {1}.", validValues, Subject); @@ -116,7 +119,7 @@ public AndConstraint BeOneOf(IEnumerable validValues, public AndConstraint BeEquivalentTo(string expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var expectation = new StringValidator( + var expectation = new StringValidator(assertionChain, new StringEqualityStrategy(StringComparer.OrdinalIgnoreCase, "be equivalent to"), because, becauseArgs); @@ -149,7 +152,7 @@ public AndConstraint BeEquivalentTo(string expected, EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); - var expectation = new StringValidator( + var expectation = new StringValidator(assertionChain, new StringEqualityStrategy(options.GetStringComparerOrDefault(), "be equivalent to"), because, becauseArgs); @@ -185,7 +188,7 @@ public AndConstraint NotBeEquivalentTo(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to be equivalent to {0}{reason}, but they are.", unexpected); @@ -223,7 +226,7 @@ public AndConstraint NotBeEquivalentTo(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to be equivalent to {0}{reason}, but they are.", unexpected); @@ -246,7 +249,7 @@ public AndConstraint NotBeEquivalentTo(string unexpected, public AndConstraint NotBe(string unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to be {0}{reason}.", unexpected); @@ -298,7 +301,7 @@ public AndConstraint Match(string wildcardPattern, Guard.ThrowIfArgumentIsEmpty(wildcardPattern, nameof(wildcardPattern), "Cannot match string against an empty string. Provide a wildcard pattern or use the BeEmpty method."); - var stringWildcardMatchingValidator = new StringValidator( + var stringWildcardMatchingValidator = new StringValidator(assertionChain, new StringWildcardMatchingStrategy(), because, becauseArgs); @@ -351,7 +354,7 @@ public AndConstraint NotMatch(string wildcardPattern, Guard.ThrowIfArgumentIsEmpty(wildcardPattern, nameof(wildcardPattern), "Cannot match string against an empty string. Provide a wildcard pattern or use the NotBeEmpty method."); - var stringWildcardMatchingValidator = new StringValidator( + var stringWildcardMatchingValidator = new StringValidator(assertionChain, new StringWildcardMatchingStrategy { Negate = true @@ -407,7 +410,7 @@ public AndConstraint MatchEquivalentOf(string wildcardPattern, Guard.ThrowIfArgumentIsEmpty(wildcardPattern, nameof(wildcardPattern), "Cannot match string against an empty string. Provide a wildcard pattern or use the BeEmpty method."); - var stringWildcardMatchingValidator = new StringValidator( + var stringWildcardMatchingValidator = new StringValidator(assertionChain, new StringWildcardMatchingStrategy { IgnoreCase = true, @@ -472,7 +475,7 @@ public AndConstraint MatchEquivalentOf(string wildcardPattern, EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); - var stringWildcardMatchingValidator = new StringValidator( + var stringWildcardMatchingValidator = new StringValidator(assertionChain, new StringWildcardMatchingStrategy { IgnoreCase = options.IgnoreCase, @@ -531,7 +534,7 @@ public AndConstraint NotMatchEquivalentOf(string wildcardPattern, Guard.ThrowIfArgumentIsEmpty(wildcardPattern, nameof(wildcardPattern), "Cannot match string against an empty string. Provide a wildcard pattern or use the NotBeEmpty method."); - var stringWildcardMatchingValidator = new StringValidator( + var stringWildcardMatchingValidator = new StringValidator(assertionChain, new StringWildcardMatchingStrategy { IgnoreCase = true, @@ -597,7 +600,7 @@ public AndConstraint NotMatchEquivalentOf(string wildcardPattern, EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); - var stringWildcardMatchingValidator = new StringValidator( + var stringWildcardMatchingValidator = new StringValidator(assertionChain, new StringWildcardMatchingStrategy { IgnoreCase = options.IgnoreCase, @@ -648,7 +651,7 @@ public AndConstraint MatchRegex([RegexPattern][StringSyntax("Regex" } catch (ArgumentException) { - Execute.Assertion.FailWith("Cannot match {context:string} against {0} because it is not a valid regular expression.", + assertionChain.FailWith("Cannot match {context:string} against {0} because it is not a valid regular expression.", regularExpression); return new AndConstraint((TAssertions)this); @@ -685,7 +688,7 @@ public AndConstraint MatchRegex([RegexPattern][StringSyntax("Regex" } catch (ArgumentException) { - Execute.Assertion.FailWith("Cannot match {context:string} against {0} because it is not a valid regular expression.", + assertionChain.FailWith("Cannot match {context:string} against {0} because it is not a valid regular expression.", regularExpression); return new AndConstraint((TAssertions)this); @@ -727,17 +730,17 @@ public AndConstraint MatchRegex(Regex regularExpression, Guard.ThrowIfArgumentIsEmpty(regexStr, nameof(regularExpression), "Cannot match string against an empty string. Provide a regex pattern or use the BeEmpty method."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .UsingLineBreaks .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} to match regex {0}{reason}, but it was .", regexStr); - if (success) + if (assertionChain.Succeeded) { int actual = regularExpression.Matches(Subject!).Count; - Execute.Assertion + assertionChain .ForConstraint(occurrenceConstraint, actual) .UsingLineBreaks .BecauseOf(because, becauseArgs) @@ -775,15 +778,15 @@ public AndConstraint MatchRegex(Regex regularExpression, Guard.ThrowIfArgumentIsEmpty(regexStr, nameof(regularExpression), "Cannot match string against an empty string. Provide a regex pattern or use the BeEmpty method."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .UsingLineBreaks .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} to match regex {0}{reason}, but it was .", regexStr); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(regularExpression.IsMatch(Subject!)) .BecauseOf(because, becauseArgs) .UsingLineBreaks @@ -821,7 +824,7 @@ public AndConstraint NotMatchRegex([RegexPattern][StringSyntax("Reg } catch (ArgumentException) { - Execute.Assertion.FailWith("Cannot match {context:string} against {0} because it is not a valid regular expression.", + assertionChain.FailWith("Cannot match {context:string} against {0} because it is not a valid regular expression.", regularExpression); return new AndConstraint((TAssertions)this); @@ -856,15 +859,15 @@ public AndConstraint NotMatchRegex(Regex regularExpression, Guard.ThrowIfArgumentIsEmpty(regexStr, nameof(regularExpression), "Cannot match string against an empty regex pattern. Provide a regex pattern or use the NotBeEmpty method."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .UsingLineBreaks .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} to not match regex {0}{reason}, but it was .", regexStr); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(!regularExpression.IsMatch(Subject!)) .BecauseOf(because, becauseArgs) .UsingLineBreaks @@ -892,7 +895,7 @@ public AndConstraint StartWith(string expected, { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot compare start of string with ."); - var stringStartValidator = new StringValidator( + var stringStartValidator = new StringValidator(assertionChain, new StringStartStrategy(StringComparer.Ordinal, "start with"), because, becauseArgs); @@ -927,7 +930,7 @@ public AndConstraint NotStartWith(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(Subject != null && notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to start with {0}{reason}, but found {1}.", unexpected, Subject); @@ -953,7 +956,7 @@ public AndConstraint StartWithEquivalentOf(string expected, { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot compare string start equivalence with ."); - var stringStartValidator = new StringValidator( + var stringStartValidator = new StringValidator(assertionChain, new StringStartStrategy(StringComparer.OrdinalIgnoreCase, "start with equivalent of"), because, becauseArgs); @@ -986,7 +989,7 @@ public AndConstraint StartWithEquivalentOf(string expected, EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); - var stringStartValidator = new StringValidator( + var stringStartValidator = new StringValidator(assertionChain, new StringStartStrategy(options.GetStringComparerOrDefault(), "start with equivalent of"), because, becauseArgs); @@ -1023,7 +1026,7 @@ public AndConstraint NotStartWithEquivalentOf(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(Subject != null && notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to start with equivalent of {0}{reason}, but found {1}.", unexpected, Subject); @@ -1061,7 +1064,7 @@ public AndConstraint NotStartWithEquivalentOf(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(Subject != null && notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to start with equivalent of {0}{reason}, but found {1}.", unexpected, Subject); @@ -1087,7 +1090,7 @@ public AndConstraint EndWith(string expected, { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot compare string end with ."); - var stringEndValidator = new StringValidator( + var stringEndValidator = new StringValidator(assertionChain, new StringEndStrategy(StringComparer.Ordinal, "end with"), because, becauseArgs); @@ -1122,7 +1125,7 @@ public AndConstraint NotEndWith(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(Subject != null && notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to end with {0}{reason}, but found {1}.", unexpected, Subject); @@ -1148,7 +1151,7 @@ public AndConstraint EndWithEquivalentOf(string expected, { Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot compare string end equivalence with ."); - var stringEndValidator = new StringValidator( + var stringEndValidator = new StringValidator(assertionChain, new StringEndStrategy(StringComparer.OrdinalIgnoreCase, "end with equivalent of"), because, becauseArgs); @@ -1181,7 +1184,7 @@ public AndConstraint EndWithEquivalentOf(string expected, EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); - var stringEndValidator = new StringValidator( + var stringEndValidator = new StringValidator(assertionChain, new StringEndStrategy(options.GetStringComparerOrDefault(), "end with equivalent of"), because, becauseArgs); @@ -1218,7 +1221,7 @@ public AndConstraint NotEndWithEquivalentOf(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(Subject != null && notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to end with equivalent of {0}{reason}, but found {1}.", unexpected, Subject); @@ -1256,7 +1259,7 @@ public AndConstraint NotEndWithEquivalentOf(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(Subject != null && notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to end with equivalent of {0}{reason}, but found {1}.", unexpected, Subject); @@ -1285,7 +1288,7 @@ public AndConstraint Contain(string expected, Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot assert string containment against ."); Guard.ThrowIfArgumentIsEmpty(expected, nameof(expected), "Cannot assert string containment against an empty string."); - Execute.Assertion + assertionChain .ForCondition(Contains(Subject, expected, StringComparison.Ordinal)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} {0} to contain {1}{reason}.", Subject, expected); @@ -1322,7 +1325,7 @@ public AndConstraint Contain(string expected, OccurrenceConstraint int actual = Subject.CountSubstring(expected, StringComparer.Ordinal); - Execute.Assertion + assertionChain .ForConstraint(occurrenceConstraint, actual) .BecauseOf(because, becauseArgs) .FailWith( @@ -1352,7 +1355,7 @@ public AndConstraint ContainEquivalentOf(string expected, Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot assert string containment against ."); Guard.ThrowIfArgumentIsEmpty(expected, nameof(expected), "Cannot assert string containment against an empty string."); - var stringContainValidator = new StringValidatorSupportingNull( + var stringContainValidator = new StringValidatorSupportingNull(assertionChain, new StringContainsStrategy(StringComparer.OrdinalIgnoreCase, AtLeast.Once()), because, becauseArgs); @@ -1418,7 +1421,7 @@ public AndConstraint ContainEquivalentOf(string expected, EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); - var stringContainValidator = new StringValidatorSupportingNull( + var stringContainValidator = new StringValidatorSupportingNull(assertionChain, new StringContainsStrategy(options.GetStringComparerOrDefault(), occurrenceConstraint), because, becauseArgs); @@ -1459,7 +1462,7 @@ public AndConstraint ContainEquivalentOf(string expected, Guard.ThrowIfArgumentIsEmpty(expected, nameof(expected), "Cannot assert string containment against an empty string."); Guard.ThrowIfArgumentIsNull(occurrenceConstraint); - var stringContainValidator = new StringValidatorSupportingNull( + var stringContainValidator = new StringValidatorSupportingNull(assertionChain, new StringContainsStrategy(StringComparer.OrdinalIgnoreCase, occurrenceConstraint), because, becauseArgs); @@ -1488,7 +1491,7 @@ public AndConstraint ContainAll(IEnumerable values, IEnumerable missing = values.Where(v => !Contains(Subject, v, StringComparison.Ordinal)); - Execute.Assertion + assertionChain .ForCondition(values.All(v => Contains(Subject, v, StringComparison.Ordinal))) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} {0} to contain the strings: {1}{reason}.", Subject, missing); @@ -1525,7 +1528,7 @@ public AndConstraint ContainAny(IEnumerable values, { ThrowIfValuesNullOrEmpty(values); - Execute.Assertion + assertionChain .ForCondition(values.Any(v => Contains(Subject, v, StringComparison.Ordinal))) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} {0} to contain at least one of the strings: {1}{reason}.", Subject, values); @@ -1565,7 +1568,7 @@ public AndConstraint NotContain(string unexpected, Guard.ThrowIfArgumentIsNull(unexpected, nameof(unexpected), "Cannot assert string containment against ."); Guard.ThrowIfArgumentIsEmpty(unexpected, nameof(unexpected), "Cannot assert string containment against an empty string."); - Execute.Assertion + assertionChain .ForCondition(!Contains(Subject, unexpected, StringComparison.Ordinal)) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} {0} to contain {1}{reason}.", Subject, unexpected); @@ -1594,7 +1597,7 @@ public AndConstraint NotContainAll(IEnumerable values, var matches = values.Count(v => Contains(Subject, v, StringComparison.Ordinal)); - Execute.Assertion + assertionChain .ForCondition(matches != values.Count()) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} {0} to contain all of the strings: {1}{reason}.", Subject, values); @@ -1634,7 +1637,7 @@ public AndConstraint NotContainAny(IEnumerable values, IEnumerable matches = values.Where(v => Contains(Subject, v, StringComparison.Ordinal)); - Execute.Assertion + assertionChain .ForCondition(!matches.Any()) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} {0} to contain any of the strings: {1}{reason}.", Subject, matches); @@ -1668,7 +1671,7 @@ public AndConstraint NotContainAny(params string[] values) public AndConstraint NotContainEquivalentOf(string unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!string.IsNullOrEmpty(unexpected) && Subject != null) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} to contain the equivalent of {0}{reason}, but found {1}.", unexpected, Subject); @@ -1681,7 +1684,7 @@ public AndConstraint NotContainEquivalentOf(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} to contain the equivalent of {0}{reason} but found {1}.", unexpected, Subject); @@ -1709,7 +1712,7 @@ public AndConstraint NotContainEquivalentOf(string unexpected, { Guard.ThrowIfArgumentIsNull(config); - Execute.Assertion + assertionChain .ForCondition(!string.IsNullOrEmpty(unexpected) && Subject != null) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} to contain the equivalent of {0}{reason}, but found {1}.", unexpected, Subject); @@ -1722,7 +1725,7 @@ public AndConstraint NotContainEquivalentOf(string unexpected, notEquivalent = scope.Discard().Length > 0; } - Execute.Assertion + assertionChain .ForCondition(notEquivalent) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} to contain the equivalent of {0}{reason}, but found {1}.", unexpected, Subject); @@ -1742,7 +1745,7 @@ public AndConstraint NotContainEquivalentOf(string unexpected, /// public AndConstraint BeEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject?.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} to be empty{reason}, but found {0}.", Subject); @@ -1762,7 +1765,7 @@ public AndConstraint BeEmpty([StringSyntax("CompositeFormat")] stri /// public AndConstraint NotBeEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is null || Subject.Length > 0) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:string} to be empty{reason}."); @@ -1784,14 +1787,14 @@ public AndConstraint NotBeEmpty([StringSyntax("CompositeFormat")] s public AndConstraint HaveLength(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:string} with length {0}{reason}, but found .", expected); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.Length == expected) .FailWith("Expected {context:string} with length {0}{reason}, but found string {1} with length {2}.", @@ -1813,7 +1816,7 @@ public AndConstraint HaveLength(int expected, /// public AndConstraint NotBeNullOrEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!string.IsNullOrEmpty(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to be or empty{reason}, but found {0}.", Subject); @@ -1833,7 +1836,7 @@ public AndConstraint NotBeNullOrEmpty([StringSyntax("CompositeForma /// public AndConstraint BeNullOrEmpty([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(string.IsNullOrEmpty(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} to be or empty{reason}, but found {0}.", Subject); @@ -1853,7 +1856,7 @@ public AndConstraint BeNullOrEmpty([StringSyntax("CompositeFormat") /// public AndConstraint NotBeNullOrWhiteSpace([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!string.IsNullOrWhiteSpace(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} not to be or whitespace{reason}, but found {0}.", Subject); @@ -1873,7 +1876,7 @@ public AndConstraint NotBeNullOrWhiteSpace([StringSyntax("Composite /// public AndConstraint BeNullOrWhiteSpace([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(string.IsNullOrWhiteSpace(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:string} to be or whitespace{reason}, but found {0}.", Subject); @@ -1898,7 +1901,7 @@ public AndConstraint BeNullOrWhiteSpace([StringSyntax("CompositeFor /// public AndConstraint BeUpperCased([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is not null && !Subject.Any(char.IsLower)) .BecauseOf(because, becauseArgs) .FailWith("Expected all alphabetic characters in {context:string} to be upper-case{reason}, but found {0}.", Subject); @@ -1923,7 +1926,7 @@ public AndConstraint BeUpperCased([StringSyntax("CompositeFormat")] /// public AndConstraint NotBeUpperCased([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is null || HasMixedOrNoCase(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected some characters in {context:string} to be lower-case{reason}."); @@ -1948,7 +1951,7 @@ public AndConstraint NotBeUpperCased([StringSyntax("CompositeFormat /// public AndConstraint BeLowerCased([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is not null && !Subject.Any(char.IsUpper)) .BecauseOf(because, becauseArgs) .FailWith("Expected all alphabetic characters in {context:string} to be lower cased{reason}, but found {0}.", Subject); @@ -1973,7 +1976,7 @@ public AndConstraint BeLowerCased([StringSyntax("CompositeFormat")] /// public AndConstraint NotBeLowerCased([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject is null || HasMixedOrNoCase(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected some characters in {context:string} to be upper-case{reason}."); @@ -2008,7 +2011,7 @@ internal AndConstraint Be(string expected, EquivalencyOptions options = config(AssertionOptions.CloneDefaults()); - var expectation = new StringValidator( + var expectation = new StringValidator(assertionChain, new StringEqualityStrategy(options.GetStringComparerOrDefault(), "be"), because, becauseArgs); diff --git a/Src/FluentAssertions/Primitives/StringContainsStrategy.cs b/Src/FluentAssertions/Primitives/StringContainsStrategy.cs index 46c8b5b2e1..c82a053b73 100644 --- a/Src/FluentAssertions/Primitives/StringContainsStrategy.cs +++ b/Src/FluentAssertions/Primitives/StringContainsStrategy.cs @@ -17,11 +17,11 @@ public StringContainsStrategy(IEqualityComparer comparer, OccurrenceCons public string ExpectationDescription => "Expected {context:string} to contain the equivalent of "; - public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, string expected) + public void ValidateAgainstMismatch(AssertionChain assertionChain, string subject, string expected) { int actual = subject.CountSubstring(expected, comparer); - assertion + assertionChain .ForConstraint(occurrenceConstraint, actual) .FailWith( $"Expected {{context:string}} {{0}} to contain the equivalent of {{1}} {{expectedOccurrence}}{{reason}}, but found it {actual.Times()}.", diff --git a/Src/FluentAssertions/Primitives/StringEndStrategy.cs b/Src/FluentAssertions/Primitives/StringEndStrategy.cs index bdb59f9cbf..a2a332491f 100644 --- a/Src/FluentAssertions/Primitives/StringEndStrategy.cs +++ b/Src/FluentAssertions/Primitives/StringEndStrategy.cs @@ -17,11 +17,13 @@ public StringEndStrategy(IEqualityComparer comparer, string predicateDes public string ExpectationDescription => "Expected {context:string} to " + predicateDescription + " "; - public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, string expected) + public void ValidateAgainstMismatch(AssertionChain assertionChain, string subject, string expected) { - if (!assertion - .ForCondition(subject!.Length >= expected.Length) - .FailWith(ExpectationDescription + "{0}{reason}, but {1} is too short.", expected, subject)) + assertionChain + .ForCondition(subject!.Length >= expected.Length) + .FailWith(ExpectationDescription + "{0}{reason}, but {1} is too short.", expected, subject); + + if (!assertionChain.Succeeded) { return; } @@ -33,7 +35,7 @@ public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, s return; } - assertion.FailWith( + assertionChain.FailWith( ExpectationDescription + "{0}{reason}, but {1} differs near " + subject.IndexedSegmentAt(indexOfMismatch) + ".", expected, subject); diff --git a/Src/FluentAssertions/Primitives/StringEqualityStrategy.cs b/Src/FluentAssertions/Primitives/StringEqualityStrategy.cs index 37ad2a72e5..d2c00a4ac0 100644 --- a/Src/FluentAssertions/Primitives/StringEqualityStrategy.cs +++ b/Src/FluentAssertions/Primitives/StringEqualityStrategy.cs @@ -18,9 +18,9 @@ public StringEqualityStrategy(IEqualityComparer comparer, string predica this.predicateDescription = predicateDescription; } - public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, string expected) + public void ValidateAgainstMismatch(AssertionChain assertionChain, string subject, string expected) { - ValidateAgainstSuperfluousWhitespace(assertion, subject, expected); + ValidateAgainstSuperfluousWhitespace(assertionChain, subject, expected); if (expected.IsLongOrMultiline() || subject.IsLongOrMultiline()) { @@ -28,7 +28,7 @@ public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, s if (indexOfMismatch == -1) { - ValidateAgainstLengthDifferences(assertion, subject, expected); + ValidateAgainstLengthDifferences(assertionChain, subject, expected); return; } @@ -43,18 +43,18 @@ public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, s locationDescription = $"on line {lineNumber + 1} and column {column} (index {indexOfMismatch})"; } - assertion.FailWith( + assertionChain.FailWith( ExpectationDescription + "the same string{reason}, but they differ " + locationDescription + ":" + Environment.NewLine + GetMismatchSegmentForLongStrings(subject, expected, indexOfMismatch) + "."); } - else if (ValidateAgainstLengthDifferences(assertion, subject, expected)) + else if (ValidateAgainstLengthDifferences(assertionChain, subject, expected)) { int indexOfMismatch = subject.IndexOfFirstMismatch(expected, comparer); if (indexOfMismatch != -1) { - assertion.FailWith( + assertionChain.FailWith( ExpectationDescription + "{0}{reason}, but {1} differs near " + subject.IndexedSegmentAt(indexOfMismatch) + ".", expected, subject); @@ -64,7 +64,7 @@ public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, s public string ExpectationDescription => "Expected {context:string} to " + predicateDescription + " "; - private void ValidateAgainstSuperfluousWhitespace(IAssertionScope assertion, string subject, string expected) + private void ValidateAgainstSuperfluousWhitespace(AssertionChain assertion, string subject, string expected) { assertion .ForCondition(!(expected.Length > subject.Length && comparer.Equals(expected.TrimEnd(), subject))) @@ -74,9 +74,9 @@ private void ValidateAgainstSuperfluousWhitespace(IAssertionScope assertion, str .FailWith(ExpectationDescription + "{0}{reason}, but it has unexpected whitespace at the end.", expected); } - private bool ValidateAgainstLengthDifferences(IAssertionScope assertion, string subject, string expected) + private bool ValidateAgainstLengthDifferences(AssertionChain assertion, string subject, string expected) { - return assertion + assertion .ForCondition(subject.Length == expected.Length) .FailWith(() => { @@ -87,6 +87,8 @@ private bool ValidateAgainstLengthDifferences(IAssertionScope assertion, string return new FailReason(message, expected, expected.Length, subject, subject.Length); }); + + return assertion.Succeeded; } private string GetMismatchSegmentForStringsOfDifferentLengths(string subject, string expected) diff --git a/Src/FluentAssertions/Primitives/StringStartStrategy.cs b/Src/FluentAssertions/Primitives/StringStartStrategy.cs index e8bd42c53d..40b6b7e834 100644 --- a/Src/FluentAssertions/Primitives/StringStartStrategy.cs +++ b/Src/FluentAssertions/Primitives/StringStartStrategy.cs @@ -17,11 +17,13 @@ public StringStartStrategy(IEqualityComparer comparer, string predicateD public string ExpectationDescription => "Expected {context:string} to " + predicateDescription + " "; - public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, string expected) + public void ValidateAgainstMismatch(AssertionChain assertionChain, string subject, string expected) { - if (!assertion - .ForCondition(subject.Length >= expected.Length) - .FailWith(ExpectationDescription + "{0}{reason}, but {1} is too short.", expected, subject)) + assertionChain + .ForCondition(subject.Length >= expected.Length) + .FailWith(ExpectationDescription + "{0}{reason}, but {1} is too short.", expected, subject); + + if (!assertionChain.Succeeded) { return; } @@ -33,7 +35,7 @@ public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, s return; } - assertion.FailWith( + assertionChain.FailWith( ExpectationDescription + "{0}{reason}, but {1} differs near " + subject.IndexedSegmentAt(indexOfMismatch) + ".", expected, subject); diff --git a/Src/FluentAssertions/Primitives/StringValidator.cs b/Src/FluentAssertions/Primitives/StringValidator.cs index 8e7e49d7a0..e3402dde81 100644 --- a/Src/FluentAssertions/Primitives/StringValidator.cs +++ b/Src/FluentAssertions/Primitives/StringValidator.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using FluentAssertions.Common; using FluentAssertions.Execution; @@ -7,12 +6,13 @@ namespace FluentAssertions.Primitives; internal class StringValidator { private readonly IStringComparisonStrategy comparisonStrategy; - private IAssertionScope assertion; + private AssertionChain assertionChain; - public StringValidator(IStringComparisonStrategy comparisonStrategy, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) + public StringValidator(AssertionChain assertionChain, IStringComparisonStrategy comparisonStrategy, string because, + object[] becauseArgs) { this.comparisonStrategy = comparisonStrategy; - assertion = Execute.Assertion.BecauseOf(because, becauseArgs); + this.assertionChain = assertionChain.BecauseOf(because, becauseArgs); } public void Validate(string subject, string expected) @@ -29,10 +29,10 @@ public void Validate(string subject, string expected) if (expected.IsLongOrMultiline() || subject.IsLongOrMultiline()) { - assertion = assertion.UsingLineBreaks; + assertionChain = assertionChain.UsingLineBreaks; } - comparisonStrategy.ValidateAgainstMismatch(assertion, subject, expected); + comparisonStrategy.ValidateAgainstMismatch(assertionChain, subject, expected); } private bool ValidateAgainstNulls(string subject, string expected) @@ -42,7 +42,7 @@ private bool ValidateAgainstNulls(string subject, string expected) return true; } - assertion.FailWith(comparisonStrategy.ExpectationDescription + "{0}{reason}, but found {1}.", expected, subject); + assertionChain.FailWith(comparisonStrategy.ExpectationDescription + "{0}{reason}, but found {1}.", expected, subject); return false; } } diff --git a/Src/FluentAssertions/Primitives/StringValidatorSupportingNull.cs b/Src/FluentAssertions/Primitives/StringValidatorSupportingNull.cs index deaf451a21..f0a20b55e7 100644 --- a/Src/FluentAssertions/Primitives/StringValidatorSupportingNull.cs +++ b/Src/FluentAssertions/Primitives/StringValidatorSupportingNull.cs @@ -7,12 +7,13 @@ namespace FluentAssertions.Primitives; internal class StringValidatorSupportingNull { private readonly IStringComparisonStrategy comparisonStrategy; - private IAssertionScope assertion; + private AssertionChain assertionChain; - public StringValidatorSupportingNull(IStringComparisonStrategy comparisonStrategy, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) + public StringValidatorSupportingNull(AssertionChain assertionChain, IStringComparisonStrategy comparisonStrategy, + [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) { this.comparisonStrategy = comparisonStrategy; - assertion = Execute.Assertion.BecauseOf(because, becauseArgs); + this.assertionChain = assertionChain.BecauseOf(because, becauseArgs); } public void Validate(string subject, string expected) @@ -25,9 +26,9 @@ public void Validate(string subject, string expected) if (expected?.IsLongOrMultiline() == true || subject?.IsLongOrMultiline() == true) { - assertion = assertion.UsingLineBreaks; + assertionChain = assertionChain.UsingLineBreaks; } - comparisonStrategy.ValidateAgainstMismatch(assertion, subject, expected); + comparisonStrategy.ValidateAgainstMismatch(assertionChain, subject, expected); } } diff --git a/Src/FluentAssertions/Primitives/StringWildcardMatchingStrategy.cs b/Src/FluentAssertions/Primitives/StringWildcardMatchingStrategy.cs index 82afb98462..eca22d9815 100644 --- a/Src/FluentAssertions/Primitives/StringWildcardMatchingStrategy.cs +++ b/Src/FluentAssertions/Primitives/StringWildcardMatchingStrategy.cs @@ -8,7 +8,7 @@ namespace FluentAssertions.Primitives; internal class StringWildcardMatchingStrategy : IStringComparisonStrategy { - public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, string expected) + public void ValidateAgainstMismatch(AssertionChain assertionChain, string subject, string expected) { bool isMatch = IsMatch(subject, expected); @@ -19,11 +19,11 @@ public void ValidateAgainstMismatch(IAssertionScope assertion, string subject, s if (Negate) { - assertion.FailWith(ExpectationDescription + "but {1} matches.", expected, subject); + assertionChain.FailWith(ExpectationDescription + "but {1} matches.", expected, subject); } else { - assertion.FailWith(ExpectationDescription + "but {1} does not.", expected, subject); + assertionChain.FailWith(ExpectationDescription + "but {1} does not.", expected, subject); } } diff --git a/Src/FluentAssertions/Primitives/TimeOnlyAssertions.cs b/Src/FluentAssertions/Primitives/TimeOnlyAssertions.cs index ce959601fe..ed92a9c0cb 100644 --- a/Src/FluentAssertions/Primitives/TimeOnlyAssertions.cs +++ b/Src/FluentAssertions/Primitives/TimeOnlyAssertions.cs @@ -16,8 +16,8 @@ namespace FluentAssertions.Primitives; [DebuggerNonUserCode] public class TimeOnlyAssertions : TimeOnlyAssertions { - public TimeOnlyAssertions(TimeOnly? value) - : base(value) + public TimeOnlyAssertions(TimeOnly? value, AssertionChain assertionChain) + : base(value, assertionChain) { } } @@ -31,8 +31,11 @@ public TimeOnlyAssertions(TimeOnly? value) public class TimeOnlyAssertions where TAssertions : TimeOnlyAssertions { - public TimeOnlyAssertions(TimeOnly? value) + private readonly AssertionChain assertionChain; + + public TimeOnlyAssertions(TimeOnly? value, AssertionChain assertionChain) { + this.assertionChain = assertionChain; Subject = value; } @@ -55,7 +58,7 @@ public TimeOnlyAssertions(TimeOnly? value) public AndConstraint Be(TimeOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be {0}{reason}, but found {1}.", @@ -78,7 +81,7 @@ public AndConstraint Be(TimeOnly expected, public AndConstraint Be(TimeOnly? expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be {0}{reason}, but found {1}.", @@ -101,7 +104,7 @@ public AndConstraint Be(TimeOnly? expected, public AndConstraint NotBe(TimeOnly unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} not to be {0}{reason}, but it is.", unexpected); @@ -123,7 +126,7 @@ public AndConstraint NotBe(TimeOnly unexpected, public AndConstraint NotBe(TimeOnly? unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject != unexpected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} not to be {0}{reason}, but it is.", unexpected); @@ -158,16 +161,15 @@ public AndConstraint BeCloseTo(TimeOnly nearbyTime, TimeSpan precis ? MinimumDifference(Subject.Value, nearbyTime) : null; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected {context:the time} to be within {0} from {1}{reason}, ", precision, nearbyTime) - .ForCondition(Subject is not null) - .FailWith("but found .") - .Then - .ForCondition(Subject?.IsCloseTo(nearbyTime, precision) == true) - .FailWith("but {0} was off by {1}.", Subject, difference) - .Then - .ClearExpectation(); + .WithExpectation("Expected {context:the time} to be within {0} from {1}{reason}, ", precision, nearbyTime, + chain => chain + .ForCondition(Subject is not null) + .FailWith("but found .") + .Then + .ForCondition(Subject?.IsCloseTo(nearbyTime, precision) == true) + .FailWith("but {0} was off by {1}.", Subject, difference)); return new AndConstraint((TAssertions)this); } @@ -203,16 +205,15 @@ public AndConstraint NotBeCloseTo(TimeOnly distantTime, TimeSpan pr { Guard.ThrowIfArgumentIsNegative(precision); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect {context:the time} to be within {0} from {1}{reason}, ", precision, distantTime) - .ForCondition(Subject is not null) - .FailWith("but found .") - .Then - .ForCondition(Subject?.IsCloseTo(distantTime, precision) == false) - .FailWith("but it was {0}.", Subject) - .Then - .ClearExpectation(); + .WithExpectation("Did not expect {context:the time} to be within {0} from {1}{reason}, ", precision, distantTime, + chain => chain + .ForCondition(Subject is not null) + .FailWith("but found .") + .Then + .ForCondition(Subject?.IsCloseTo(distantTime, precision) == false) + .FailWith("but it was {0}.", Subject)); return new AndConstraint((TAssertions)this); } @@ -231,7 +232,7 @@ public AndConstraint NotBeCloseTo(TimeOnly distantTime, TimeSpan pr public AndConstraint BeBefore(TimeOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject < expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be before {0}{reason}, but found {1}.", expected, @@ -271,7 +272,7 @@ public AndConstraint NotBeBefore(TimeOnly unexpected, public AndConstraint BeOnOrBefore(TimeOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject <= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be on or before {0}{reason}, but found {1}.", expected, @@ -311,7 +312,7 @@ public AndConstraint NotBeOnOrBefore(TimeOnly unexpected, public AndConstraint BeAfter(TimeOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject > expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be after {0}{reason}, but found {1}.", expected, @@ -351,7 +352,7 @@ public AndConstraint NotBeAfter(TimeOnly unexpected, public AndConstraint BeOnOrAfter(TimeOnly expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject >= expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be on or after {0}{reason}, but found {1}.", expected, @@ -391,16 +392,14 @@ public AndConstraint NotBeOnOrAfter(TimeOnly unexpected, public AndConstraint HaveHours(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the hours part of {context:the time} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found .") - .Then - .ForCondition(Subject.Value.Hour == expected) - .FailWith(", but found {0}.", Subject.Value.Hour) - .Then - .ClearExpectation(); + .WithExpectation("Expected the hours part of {context:the time} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found .") + .Then + .ForCondition(Subject.Value.Hour == expected) + .FailWith(", but found {0}.", Subject.Value.Hour)); return new AndConstraint((TAssertions)this); } @@ -419,7 +418,7 @@ public AndConstraint HaveHours(int expected, public AndConstraint NotHaveHours(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.HasValue) .FailWith("Did not expect the hours part of {context:the time} to be {0}{reason}, but found a TimeOnly.", @@ -446,16 +445,14 @@ public AndConstraint NotHaveHours(int unexpected, public AndConstraint HaveMinutes(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the minutes part of {context:the time} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a TimeOnly.") - .Then - .ForCondition(Subject.Value.Minute == expected) - .FailWith(", but found {0}.", Subject.Value.Minute) - .Then - .ClearExpectation(); + .WithExpectation("Expected the minutes part of {context:the time} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a TimeOnly.") + .Then + .ForCondition(Subject.Value.Minute == expected) + .FailWith(", but found {0}.", Subject.Value.Minute)); return new AndConstraint((TAssertions)this); } @@ -474,16 +471,14 @@ public AndConstraint HaveMinutes(int expected, public AndConstraint NotHaveMinutes(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the minutes part of {context:the time} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a TimeOnly.") - .Then - .ForCondition(Subject.Value.Minute != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the minutes part of {context:the time} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a TimeOnly.") + .Then + .ForCondition(Subject.Value.Minute != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -502,16 +497,14 @@ public AndConstraint NotHaveMinutes(int unexpected, public AndConstraint HaveSeconds(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the seconds part of {context:the time} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a TimeOnly.") - .Then - .ForCondition(Subject.Value.Second == expected) - .FailWith(", but found {0}.", Subject.Value.Second) - .Then - .ClearExpectation(); + .WithExpectation("Expected the seconds part of {context:the time} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a TimeOnly.") + .Then + .ForCondition(Subject.Value.Second == expected) + .FailWith(", but found {0}.", Subject.Value.Second)); return new AndConstraint((TAssertions)this); } @@ -530,16 +523,14 @@ public AndConstraint HaveSeconds(int expected, public AndConstraint NotHaveSeconds(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the seconds part of {context:the time} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a TimeOnly.") - .Then - .ForCondition(Subject.Value.Second != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the seconds part of {context:the time} to be {0}{reason}", unexpected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a TimeOnly.") + .Then + .ForCondition(Subject.Value.Second != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -558,16 +549,14 @@ public AndConstraint NotHaveSeconds(int unexpected, public AndConstraint HaveMilliseconds(int expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected the milliseconds part of {context:the time} to be {0}{reason}", expected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a TimeOnly.") - .Then - .ForCondition(Subject.Value.Millisecond == expected) - .FailWith(", but found {0}.", Subject.Value.Millisecond) - .Then - .ClearExpectation(); + .WithExpectation("Expected the milliseconds part of {context:the time} to be {0}{reason}", expected, chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a TimeOnly.") + .Then + .ForCondition(Subject.Value.Millisecond == expected) + .FailWith(", but found {0}.", Subject.Value.Millisecond)); return new AndConstraint((TAssertions)this); } @@ -586,16 +575,15 @@ public AndConstraint HaveMilliseconds(int expected, public AndConstraint NotHaveMilliseconds(int unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Did not expect the milliseconds part of {context:the time} to be {0}{reason}", unexpected) - .ForCondition(Subject.HasValue) - .FailWith(", but found a TimeOnly.") - .Then - .ForCondition(Subject.Value.Millisecond != unexpected) - .FailWith(", but it was.") - .Then - .ClearExpectation(); + .WithExpectation("Did not expect the milliseconds part of {context:the time} to be {0}{reason}", unexpected, + chain => chain + .ForCondition(Subject.HasValue) + .FailWith(", but found a TimeOnly.") + .Then + .ForCondition(Subject.Value.Millisecond != unexpected) + .FailWith(", but it was.")); return new AndConstraint((TAssertions)this); } @@ -657,7 +645,7 @@ public AndConstraint BeOneOf(IEnumerable validValues, public AndConstraint BeOneOf(IEnumerable validValues, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(validValues.Contains(Subject)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:time} to be one of {0}{reason}, but found {1}.", validValues, Subject); diff --git a/Src/FluentAssertions/Specialized/ActionAssertions.cs b/Src/FluentAssertions/Specialized/ActionAssertions.cs index 0051f0dee8..94be29f8c4 100644 --- a/Src/FluentAssertions/Specialized/ActionAssertions.cs +++ b/Src/FluentAssertions/Specialized/ActionAssertions.cs @@ -12,14 +12,18 @@ namespace FluentAssertions.Specialized; [DebuggerNonUserCode] public class ActionAssertions : DelegateAssertions { - public ActionAssertions(Action subject, IExtractExceptions extractor) - : base(subject, extractor) + private readonly AssertionChain assertionChain; + + public ActionAssertions(Action subject, IExtractExceptions extractor, AssertionChain assertionChain) + : base(subject, extractor, assertionChain) { + this.assertionChain = assertionChain; } - public ActionAssertions(Action subject, IExtractExceptions extractor, IClock clock) - : base(subject, extractor, clock) + public ActionAssertions(Action subject, IExtractExceptions extractor, AssertionChain assertionChain, IClock clock) + : base(subject, extractor, assertionChain, clock) { + this.assertionChain = assertionChain; } /// @@ -34,12 +38,12 @@ public ActionAssertions(Action subject, IExtractExceptions extractor, IClock clo /// public AndConstraint NotThrow([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw{reason}, but found ."); - if (success) + if (assertionChain.Succeeded) { FailIfSubjectIsAsyncVoid(); Exception exception = InvokeSubjectWithInterception(); @@ -78,12 +82,12 @@ public AndConstraint NotThrowAfter(TimeSpan waitTime, TimeSpan Guard.ThrowIfArgumentIsNegative(waitTime); Guard.ThrowIfArgumentIsNegative(pollInterval); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw after {0}{reason}, but found .", waitTime); - if (success) + if (assertionChain.Succeeded) { FailIfSubjectIsAsyncVoid(); @@ -104,7 +108,7 @@ public AndConstraint NotThrowAfter(TimeSpan waitTime, TimeSpan invocationEndTime = timer.Elapsed; } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(exception is null) .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); diff --git a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs index 30791f52a2..7a120af2a0 100644 --- a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs @@ -19,9 +19,13 @@ public class AsyncFunctionAssertions : DelegateAssertionsBas where TTask : Task where TAssertions : AsyncFunctionAssertions { - protected AsyncFunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) - : base(subject, extractor, clock) + private readonly AssertionChain assertionChain; + + protected AsyncFunctionAssertions(Func subject, IExtractExceptions extractor, AssertionChain assertionChain, + IClock clock) + : base(subject, extractor, assertionChain, clock) { + this.assertionChain = assertionChain; } protected override string Identifier => "async function"; @@ -40,12 +44,12 @@ protected AsyncFunctionAssertions(Func subject, IExtractExceptions extrac public async Task> NotCompleteWithinAsync(TimeSpan timeSpan, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:task} to complete within {0}{reason}, but found .", timeSpan); - if (success) + if (assertionChain.Succeeded) { (Task task, TimeSpan remainingTime) = InvokeWithTimer(timeSpan); @@ -53,7 +57,7 @@ public async Task> NotCompleteWithinAsync(TimeSpan ti { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime); - Execute.Assertion + assertionChain .ForCondition(!completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:task} to complete within {0}{reason}.", timeSpan); @@ -83,29 +87,29 @@ public async Task> ThrowExactlyAsync { Type expectedType = typeof(TException); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to throw exactly {0}{reason}, but found .", expectedType); - if (success) + if (assertionChain.Succeeded) { Exception exception = await InvokeWithInterceptionAsync(Subject); - success = Execute.Assertion + assertionChain .ForCondition(exception is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {0}{reason}, but no exception was thrown.", expectedType); - if (success) + if (assertionChain.Succeeded) { exception.Should().BeOfType(expectedType, because, becauseArgs); } - return new ExceptionAssertions([exception as TException]); + return new ExceptionAssertions([exception as TException], assertionChain); } - return new ExceptionAssertions([]); + return new ExceptionAssertions([], assertionChain); } /// @@ -123,18 +127,18 @@ public async Task> ThrowAsync( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TException : Exception { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to throw {0}{reason}, but found .", typeof(TException)); - if (success) + if (assertionChain.Succeeded) { Exception exception = await InvokeWithInterceptionAsync(Subject); return ThrowInternal(exception, because, becauseArgs); } - return new ExceptionAssertions([]); + return new ExceptionAssertions([], assertionChain); } /// @@ -154,19 +158,19 @@ public async Task> ThrowWithinAsync( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TException : Exception { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to throw {0} within {1}{reason}, but found .", typeof(TException), timeSpan); - if (success) + if (assertionChain.Succeeded) { Exception caughtException = await InvokeWithInterceptionAsync(timeSpan); return AssertThrows(caughtException, timeSpan, because, becauseArgs); } - return new ExceptionAssertions([]); + return new ExceptionAssertions([], assertionChain); } private ExceptionAssertions AssertThrows( @@ -176,21 +180,18 @@ private ExceptionAssertions AssertThrows( { TException[] expectedExceptions = Extractor.OfType(exception).ToArray(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected a <{0}> to be thrown within {1}{reason}, ", - typeof(TException), timeSpan) - .ForCondition(exception is not null) - .FailWith("but no exception was thrown.") - .Then - .ForCondition(expectedExceptions.Length > 0) - .FailWith("but found <{0}>:" + Environment.NewLine + "{1}.", - exception?.GetType(), - exception) - .Then - .ClearExpectation(); - - return new ExceptionAssertions(expectedExceptions); + .WithExpectation("Expected a <{0}> to be thrown within {1}{reason}, ", typeof(TException), timeSpan, chain => chain + .ForCondition(exception is not null) + .FailWith("but no exception was thrown.") + .Then + .ForCondition(expectedExceptions.Length > 0) + .FailWith("but found <{0}>:" + Environment.NewLine + "{1}.", + exception?.GetType(), + exception)); + + return new ExceptionAssertions(expectedExceptions, assertionChain); } private async Task InvokeWithInterceptionAsync(TimeSpan timeout) @@ -210,6 +211,7 @@ private async Task InvokeWithInterceptionAsync(TimeSpan timeout) : default) { (TTask task, TimeSpan remainingTime) = InvokeWithTimer(timeout); + if (remainingTime < TimeSpan.Zero) { // timeout reached without exception @@ -248,15 +250,16 @@ private async Task InvokeWithInterceptionAsync(TimeSpan timeout) /// /// Zero or more objects to format using the placeholders in . /// - public async Task> NotThrowAsync([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public async Task> NotThrowAsync([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) where TException : Exception { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw{reason}, but found ."); - if (success) + if (assertionChain.Succeeded) { try { diff --git a/Src/FluentAssertions/Specialized/DelegateAssertions.cs b/Src/FluentAssertions/Specialized/DelegateAssertions.cs index a8084e3255..f10a4b5885 100644 --- a/Src/FluentAssertions/Specialized/DelegateAssertions.cs +++ b/Src/FluentAssertions/Specialized/DelegateAssertions.cs @@ -16,14 +16,18 @@ public abstract class DelegateAssertions : DelegateAsser where TDelegate : Delegate where TAssertions : DelegateAssertions { - protected DelegateAssertions(TDelegate @delegate, IExtractExceptions extractor) - : base(@delegate, extractor, new Clock()) + private readonly AssertionChain assertionChain; + + protected DelegateAssertions(TDelegate @delegate, IExtractExceptions extractor, AssertionChain assertionChain) + : base(@delegate, extractor, assertionChain, new Clock()) { + this.assertionChain = assertionChain; } - private protected DelegateAssertions(TDelegate @delegate, IExtractExceptions extractor, IClock clock) - : base(@delegate, extractor, clock) + private protected DelegateAssertions(TDelegate @delegate, IExtractExceptions extractor, AssertionChain assertionChain, IClock clock) + : base(@delegate, extractor, assertionChain, clock) { + this.assertionChain = assertionChain; } /// @@ -39,19 +43,19 @@ private protected DelegateAssertions(TDelegate @delegate, IExtractExceptions ext public ExceptionAssertions Throw([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TException : Exception { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to throw {0}{reason}, but found .", typeof(TException)); - if (success) + if (assertionChain.Succeeded) { FailIfSubjectIsAsyncVoid(); Exception exception = InvokeSubjectWithInterception(); return ThrowInternal(exception, because, becauseArgs); } - return new ExceptionAssertions([]); + return new ExceptionAssertions([], assertionChain); } /// @@ -67,12 +71,12 @@ public ExceptionAssertions Throw([StringSyntax("Composit public AndConstraint NotThrow([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TException : Exception { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw {0}{reason}, but found .", typeof(TException)); - if (success) + if (assertionChain.Succeeded) { FailIfSubjectIsAsyncVoid(); Exception exception = InvokeSubjectWithInterception(); @@ -102,32 +106,32 @@ public ExceptionAssertions ThrowExactly([StringSyntax("C params object[] becauseArgs) where TException : Exception { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to throw exactly {0}{reason}, but found .", typeof(TException)); - if (success) + if (assertionChain.Succeeded) { FailIfSubjectIsAsyncVoid(); Exception exception = InvokeSubjectWithInterception(); Type expectedType = typeof(TException); - success = Execute.Assertion + assertionChain .ForCondition(exception is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {0}{reason}, but no exception was thrown.", expectedType); - if (success) + if (assertionChain.Succeeded) { exception.Should().BeOfType(expectedType, because, becauseArgs); } - return new ExceptionAssertions([exception as TException]); + return new ExceptionAssertions([exception as TException], assertionChain); } - return new ExceptionAssertions([]); + return new ExceptionAssertions([], assertionChain); } protected abstract void InvokeSubject(); diff --git a/Src/FluentAssertions/Specialized/DelegateAssertionsBase.cs b/Src/FluentAssertions/Specialized/DelegateAssertionsBase.cs index 77b687f54e..fe662c0424 100644 --- a/Src/FluentAssertions/Specialized/DelegateAssertionsBase.cs +++ b/Src/FluentAssertions/Specialized/DelegateAssertionsBase.cs @@ -18,11 +18,15 @@ public abstract class DelegateAssertionsBase where TDelegate : Delegate where TAssertions : DelegateAssertionsBase { + private readonly AssertionChain assertionChain; + private protected IExtractExceptions Extractor { get; } - private protected DelegateAssertionsBase(TDelegate @delegate, IExtractExceptions extractor, IClock clock) - : base(@delegate) + private protected DelegateAssertionsBase(TDelegate @delegate, IExtractExceptions extractor, AssertionChain assertionChain, + IClock clock) + : base(@delegate, assertionChain) { + this.assertionChain = assertionChain; Extractor = extractor ?? throw new ArgumentNullException(nameof(extractor)); Clock = clock ?? throw new ArgumentNullException(nameof(clock)); } @@ -36,25 +40,24 @@ protected ExceptionAssertions ThrowInternal( { TException[] expectedExceptions = Extractor.OfType(exception).ToArray(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected a <{0}> to be thrown{reason}, ", typeof(TException)) - .ForCondition(exception is not null) - .FailWith("but no exception was thrown.") - .Then - .ForCondition(expectedExceptions.Length > 0) - .FailWith("but found <{0}>:" + Environment.NewLine + "{1}.", - exception?.GetType(), - exception) - .Then - .ClearExpectation(); + .WithExpectation("Expected a <{0}> to be thrown{reason}, ", typeof(TException), chain => chain + .ForCondition(exception is not null) + .FailWith("but no exception was thrown.") + .Then + .ForCondition(expectedExceptions.Length > 0) + .FailWith("but found <{0}>:" + Environment.NewLine + "{1}.", + exception?.GetType(), + exception)); - return new ExceptionAssertions(expectedExceptions); + return new ExceptionAssertions(expectedExceptions, assertionChain); } - protected AndConstraint NotThrowInternal(Exception exception, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) + protected AndConstraint NotThrowInternal(Exception exception, [StringSyntax("CompositeFormat")] string because, + object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(exception is null) .BecauseOf(because, becauseArgs) .FailWith("Did not expect any exception{reason}, but found {0}.", exception); @@ -62,12 +65,13 @@ protected AndConstraint NotThrowInternal(Exception exception, [Stri return new AndConstraint((TAssertions)this); } - protected AndConstraint NotThrowInternal(Exception exception, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) + protected AndConstraint NotThrowInternal(Exception exception, + [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) where TException : Exception { IEnumerable exceptions = Extractor.OfType(exception); - Execute.Assertion + assertionChain .ForCondition(!exceptions.Any()) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {0}{reason}, but found {1}.", typeof(TException), exception); diff --git a/Src/FluentAssertions/Specialized/ExceptionAssertions.cs b/Src/FluentAssertions/Specialized/ExceptionAssertions.cs index 3cec526e9a..6c249a969d 100644 --- a/Src/FluentAssertions/Specialized/ExceptionAssertions.cs +++ b/Src/FluentAssertions/Specialized/ExceptionAssertions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using FluentAssertions.Common; @@ -19,9 +18,12 @@ namespace FluentAssertions.Specialized; public class ExceptionAssertions : ReferenceTypeAssertions, ExceptionAssertions> where TException : Exception { - public ExceptionAssertions(IEnumerable exceptions) - : base(exceptions) + private readonly AssertionChain assertionChain; + + public ExceptionAssertions(IEnumerable exceptions, AssertionChain assertionChain) + : base(exceptions, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -72,16 +74,16 @@ public ExceptionAssertions(IEnumerable exceptions) /// /// /// - public virtual ExceptionAssertions WithMessage(string expectedWildcardPattern, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public virtual ExceptionAssertions WithMessage(string expectedWildcardPattern, string because = "", + params object[] becauseArgs) { - AssertionScope assertion = Execute.Assertion.BecauseOf(because, becauseArgs).UsingLineBreaks; - - assertion + assertionChain + .BecauseOf(because, becauseArgs) + .UsingLineBreaks .ForCondition(Subject.Any()) .FailWith("Expected exception with message {0}{reason}, but no exception was thrown.", expectedWildcardPattern); - ExceptionMessageAssertion.Execute(Subject.Select(exc => exc.Message), expectedWildcardPattern, because, + AssertExceptionMessage(Subject.Select(exc => exc.Message), expectedWildcardPattern, because, becauseArgs); return this; @@ -98,12 +100,12 @@ public virtual ExceptionAssertions WithMessage(string expectedWildca /// /// Zero or more objects to format using the placeholders in . /// - public virtual ExceptionAssertions WithInnerException( - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public virtual ExceptionAssertions WithInnerException(string because = "", + params object[] becauseArgs) where TInnerException : Exception { var expectedInnerExceptions = AssertInnerExceptions(typeof(TInnerException), because, becauseArgs); - return new ExceptionAssertions(expectedInnerExceptions.Cast()); + return new ExceptionAssertions(expectedInnerExceptions.Cast(), assertionChain); } /// @@ -117,12 +119,12 @@ public virtual ExceptionAssertions WithInnerException /// Zero or more objects to format using the placeholders in . /// - public ExceptionAssertions WithInnerException(Type innerException, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public ExceptionAssertions WithInnerException(Type innerException, string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(innerException); - return new ExceptionAssertions(AssertInnerExceptions(innerException, because, becauseArgs)); + return new ExceptionAssertions(AssertInnerExceptions(innerException, because, becauseArgs), assertionChain); } /// @@ -136,12 +138,12 @@ public ExceptionAssertions WithInnerException(Type innerException, /// /// Zero or more objects to format using the placeholders in . /// - public virtual ExceptionAssertions WithInnerExceptionExactly([StringSyntax("CompositeFormat")] string because = "", + public virtual ExceptionAssertions WithInnerExceptionExactly(string because = "", params object[] becauseArgs) where TInnerException : Exception { var exceptionExpression = AssertInnerExceptionExactly(typeof(TInnerException), because, becauseArgs); - return new ExceptionAssertions(exceptionExpression.Cast()); + return new ExceptionAssertions(exceptionExpression.Cast(), assertionChain); } /// @@ -155,12 +157,12 @@ public virtual ExceptionAssertions WithInnerExceptionExactly /// Zero or more objects to format using the placeholders in . /// - public ExceptionAssertions WithInnerExceptionExactly(Type innerException, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public ExceptionAssertions WithInnerExceptionExactly(Type innerException, string because = "", + params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(innerException); - return new ExceptionAssertions(AssertInnerExceptionExactly(innerException, because, becauseArgs)); + return new ExceptionAssertions(AssertInnerExceptionExactly(innerException, because, becauseArgs), assertionChain); } /// @@ -178,13 +180,13 @@ public ExceptionAssertions WithInnerExceptionExactly(Type innerExcept /// /// is . public ExceptionAssertions Where(Expression> exceptionExpression, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNull(exceptionExpression); Func condition = exceptionExpression.Compile(); - Execute.Assertion + assertionChain .ForCondition(condition(SingleSubject)) .BecauseOf(because, becauseArgs) .FailWith("Expected exception where {0}{reason}, but the condition was not met by:" @@ -194,10 +196,10 @@ public ExceptionAssertions Where(Expression> return this; } - private IEnumerable AssertInnerExceptionExactly(Type innerException, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + private IEnumerable AssertInnerExceptionExactly(Type innerException, string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.Any(e => e.InnerException is not null)) .FailWith("Expected inner {0}{reason}, but the thrown exception has no inner exception.", innerException); @@ -206,7 +208,7 @@ private IEnumerable AssertInnerExceptionExactly(Type innerException, .Select(e => e.InnerException) .Where(e => e?.GetType() == innerException).ToArray(); - Execute.Assertion + assertionChain .ForCondition(expectedExceptions.Length > 0) .BecauseOf(because, becauseArgs) .FailWith("Expected inner {0}{reason}, but found {1}.", innerException, SingleSubject.InnerException); @@ -214,10 +216,10 @@ private IEnumerable AssertInnerExceptionExactly(Type innerException, return expectedExceptions; } - private IEnumerable AssertInnerExceptions(Type innerException, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + private IEnumerable AssertInnerExceptions(Type innerException, string because = "", + params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.Any(e => e.InnerException is not null)) .FailWith("Expected inner {0}{reason}, but the thrown exception has no inner exception.", innerException); @@ -227,7 +229,7 @@ private IEnumerable AssertInnerExceptions(Type innerException, .Where(e => e != null && e.GetType().IsSameOrInherits(innerException)) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(expectedInnerExceptions.Length > 0) .BecauseOf(because, becauseArgs) .FailWith("Expected inner {0}{reason}, but found {1}.", innerException, SingleSubject.InnerException); @@ -259,39 +261,35 @@ private static string BuildExceptionsString(IEnumerable exceptions) "\t" + Formatter.ToString(exception))); } - private static class ExceptionMessageAssertion + private void AssertExceptionMessage(IEnumerable messages, string expectation, string because, params object[] becauseArgs) { - private const string Context = "exception message"; + var results = new AssertionResultSet(); - public static void Execute(IEnumerable messages, string expectation, [StringSyntax("CompositeFormat")] string because, params object[] becauseArgs) + foreach (string message in messages) { - using var _ = new AssertionScope(); - var results = new AssertionResultSet(); - - foreach (string message in messages) + using (var scope = new AssertionScope()) { - using (var scope = new AssertionScope()) - { - scope.Context = new Lazy(() => Context); + var chain = AssertionChain.GetOrCreate(); + chain.OverrideCallerIdentifier(() => "exception message"); + chain.ReuseOnce(); - message.Should().MatchEquivalentOf(expectation, because, becauseArgs); + message.Should().MatchEquivalentOf(expectation, because, becauseArgs); - results.AddSet(message, scope.Discard()); - } - - if (results.ContainsSuccessfulSet()) - { - break; - } + results.AddSet(message, scope.Discard()); } - foreach (string failure in results.GetTheFailuresForTheSetWithTheFewestFailures()) + if (results.ContainsSuccessfulSet()) { - string replacedCurlyBraces = - failure.EscapePlaceholders(); - - AssertionScope.Current.FailWith(replacedCurlyBraces); + break; } } + + foreach (string failure in results.GetTheFailuresForTheSetWithTheFewestFailures()) + { + string replacedCurlyBraces = + failure.EscapePlaceholders(); + + assertionChain.FailWith(replacedCurlyBraces); + } } } diff --git a/Src/FluentAssertions/Specialized/ExecutionTimeAssertions.cs b/Src/FluentAssertions/Specialized/ExecutionTimeAssertions.cs index 1c63fcc35a..0dfaf1446d 100644 --- a/Src/FluentAssertions/Specialized/ExecutionTimeAssertions.cs +++ b/Src/FluentAssertions/Specialized/ExecutionTimeAssertions.cs @@ -13,14 +13,16 @@ namespace FluentAssertions.Specialized; public class ExecutionTimeAssertions { private readonly ExecutionTime execution; + private readonly AssertionChain assertionChain; /// /// Initializes a new instance of the class. /// /// The execution on which time must be asserted. - public ExecutionTimeAssertions(ExecutionTime executionTime) + public ExecutionTimeAssertions(ExecutionTime executionTime, AssertionChain assertionChain) { execution = executionTime ?? throw new ArgumentNullException(nameof(executionTime)); + this.assertionChain = assertionChain; } /// @@ -74,7 +76,7 @@ public AndConstraint BeLessThanOrEqualTo(TimeSpan maxDu { (bool isRunning, TimeSpan elapsed) = PollUntil(duration => duration <= maxDuration, expectedResult: false, rate: maxDuration); - Execute.Assertion + assertionChain .ForCondition(elapsed <= maxDuration) .BecauseOf(because, becauseArgs) .FailWith("Execution of " + @@ -105,7 +107,7 @@ public AndConstraint BeLessThan(TimeSpan maxDuration, { (bool isRunning, TimeSpan elapsed) = PollUntil(duration => duration < maxDuration, expectedResult: false, rate: maxDuration); - Execute.Assertion + assertionChain .ForCondition(elapsed < maxDuration) .BecauseOf(because, becauseArgs) .FailWith("Execution of " + @@ -135,7 +137,7 @@ public AndConstraint BeGreaterThanOrEqualTo(TimeSpan mi { (bool isRunning, TimeSpan elapsed) = PollUntil(duration => duration >= minDuration, expectedResult: true, rate: minDuration); - Execute.Assertion + assertionChain .ForCondition(elapsed >= minDuration) .BecauseOf(because, becauseArgs) .FailWith("Execution of " + @@ -166,7 +168,7 @@ public AndConstraint BeGreaterThan(TimeSpan minDuration { (bool isRunning, TimeSpan elapsed) = PollUntil(duration => duration > minDuration, expectedResult: true, rate: minDuration); - Execute.Assertion + assertionChain .ForCondition(elapsed > minDuration) .BecauseOf(because, becauseArgs) .FailWith("Execution of " + @@ -208,7 +210,7 @@ public AndConstraint BeCloseTo(TimeSpan expectedDuratio // elapsed time didn't even get to the acceptable range (bool isRunning, TimeSpan elapsed) = PollUntil(duration => duration <= maximumValue, expectedResult: false, rate: maximumValue); - Execute.Assertion + assertionChain .ForCondition(elapsed >= minimumValue && elapsed <= maximumValue) .BecauseOf(because, becauseArgs) .FailWith("Execution of " + execution.ActionDescription.EscapePlaceholders() + diff --git a/Src/FluentAssertions/Specialized/FunctionAssertionHelpers.cs b/Src/FluentAssertions/Specialized/FunctionAssertionHelpers.cs deleted file mode 100644 index 28c4e0983f..0000000000 --- a/Src/FluentAssertions/Specialized/FunctionAssertionHelpers.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using FluentAssertions.Common; -using FluentAssertions.Execution; - -namespace FluentAssertions.Specialized; - -internal static class FunctionAssertionHelpers -{ - internal static T NotThrow(Func subject, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) - { - try - { - return subject(); - } - catch (Exception exception) - { - Execute.Assertion - .BecauseOf(because, becauseArgs) - .FailWith("Did not expect any exception{reason}, but found {0}.", exception); - - return default; - } - } - - internal static TResult NotThrowAfter(Func subject, IClock clock, TimeSpan waitTime, TimeSpan pollInterval, - [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) - { - Guard.ThrowIfArgumentIsNegative(waitTime); - Guard.ThrowIfArgumentIsNegative(pollInterval); - - TimeSpan? invocationEndTime = null; - Exception exception = null; - ITimer timer = clock.StartTimer(); - - while (invocationEndTime is null || invocationEndTime < waitTime) - { - try - { - return subject(); - } - catch (Exception ex) - { - exception = ex; - } - - clock.Delay(pollInterval); - invocationEndTime = timer.Elapsed; - } - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); - - return default; - } -} diff --git a/Src/FluentAssertions/Specialized/FunctionAssertions.cs b/Src/FluentAssertions/Specialized/FunctionAssertions.cs index 011b961165..0219cab38f 100644 --- a/Src/FluentAssertions/Specialized/FunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/FunctionAssertions.cs @@ -12,14 +12,18 @@ namespace FluentAssertions.Specialized; [DebuggerNonUserCode] public class FunctionAssertions : DelegateAssertions, FunctionAssertions> { - public FunctionAssertions(Func subject, IExtractExceptions extractor) - : base(subject, extractor) + private readonly AssertionChain assertionChain; + + public FunctionAssertions(Func subject, IExtractExceptions extractor, AssertionChain assertionChain) + : base(subject, extractor, assertionChain) { + this.assertionChain = assertionChain; } - public FunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) - : base(subject, extractor, clock) + public FunctionAssertions(Func subject, IExtractExceptions extractor, AssertionChain assertionChain, IClock clock) + : base(subject, extractor, assertionChain, clock) { + this.assertionChain = assertionChain; } protected override void InvokeSubject() @@ -41,19 +45,30 @@ protected override void InvokeSubject() /// public AndWhichConstraint, T> NotThrow([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw{reason}, but found ."); T result = default; - if (success) + if (assertionChain.Succeeded) { - result = FunctionAssertionHelpers.NotThrow(Subject, because, becauseArgs); + try + { + result = Subject!(); + } + catch (Exception exception) + { + assertionChain + .BecauseOf(because, becauseArgs) + .FailWith("Did not expect any exception{reason}, but found {0}.", exception); + + result = default; + } } - return new AndWhichConstraint, T>(this, result); + return new AndWhichConstraint, T>(this, result, assertionChain, ".Result"); } /// @@ -82,18 +97,50 @@ public AndWhichConstraint, T> NotThrow([StringSyntax("Comp public AndWhichConstraint, T> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found .", waitTime); T result = default; - if (success) + if (assertionChain.Succeeded) { - result = FunctionAssertionHelpers.NotThrowAfter(Subject, Clock, waitTime, pollInterval, because, becauseArgs); + result = NotThrowAfter(Subject, Clock, waitTime, pollInterval, because, becauseArgs); } - return new AndWhichConstraint, T>(this, result); + return new AndWhichConstraint, T>(this, result, assertionChain, ".Result"); + } + + internal TResult NotThrowAfter(Func subject, IClock clock, TimeSpan waitTime, TimeSpan pollInterval, + string because, object[] becauseArgs) + { + Guard.ThrowIfArgumentIsNegative(waitTime); + Guard.ThrowIfArgumentIsNegative(pollInterval); + + TimeSpan? invocationEndTime = null; + Exception exception = null; + ITimer timer = clock.StartTimer(); + + while (invocationEndTime is null || invocationEndTime < waitTime) + { + try + { + return subject(); + } + catch (Exception ex) + { + exception = ex; + } + + clock.Delay(pollInterval); + invocationEndTime = timer.Elapsed; + } + + assertionChain + .BecauseOf(because, becauseArgs) + .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); + + return default; } } diff --git a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs index 109d1918b3..ea78c3b77b 100644 --- a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs @@ -14,20 +14,25 @@ namespace FluentAssertions.Specialized; public class GenericAsyncFunctionAssertions : AsyncFunctionAssertions, GenericAsyncFunctionAssertions> { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public GenericAsyncFunctionAssertions(Func> subject, IExtractExceptions extractor) - : this(subject, extractor, new Clock()) + public GenericAsyncFunctionAssertions(Func> subject, IExtractExceptions extractor, AssertionChain assertionChain) + : this(subject, extractor, assertionChain, new Clock()) { + this.assertionChain = assertionChain; } /// /// Initializes a new instance of the class with custom . /// - public GenericAsyncFunctionAssertions(Func> subject, IExtractExceptions extractor, IClock clock) - : base(subject, extractor, clock) + public GenericAsyncFunctionAssertions(Func> subject, IExtractExceptions extractor, AssertionChain assertionChain, + IClock clock) + : base(subject, extractor, assertionChain, clock) { + this.assertionChain = assertionChain; } /// @@ -44,34 +49,34 @@ public GenericAsyncFunctionAssertions(Func> subject, IExtractExcep public async Task, TResult>> CompleteWithinAsync( TimeSpan timeSpan, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to complete within {0}{reason}, but found .", timeSpan); - if (success) + if (assertionChain.Succeeded) { (Task task, TimeSpan remainingTime) = InvokeWithTimer(timeSpan); - success = Execute.Assertion + assertionChain .ForCondition(remainingTime >= TimeSpan.Zero) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); - if (success) + if (assertionChain.Succeeded) { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime); - success = Execute.Assertion + assertionChain .ForCondition(completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); } #pragma warning disable CA1849 // Call async methods when in an async method - TResult result = success ? task.Result : default; + TResult result = assertionChain.Succeeded ? task.Result : default; #pragma warning restore CA1849 // Call async methods when in an async method - return new AndWhichConstraint, TResult>(this, result); + return new AndWhichConstraint, TResult>(this, result, assertionChain, ".Result"); } return new AndWhichConstraint, TResult>(this, default(TResult)); @@ -90,17 +95,17 @@ public async Task, TR public async Task, TResult>> NotThrowAsync( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw{reason}, but found ."); - if (success) + if (assertionChain.Succeeded) { try { TResult result = await Subject!.Invoke(); - return new AndWhichConstraint, TResult>(this, result); + return new AndWhichConstraint, TResult>(this, result, assertionChain, ".Result"); } catch (Exception exception) { @@ -140,12 +145,12 @@ public Task, TResult> Guard.ThrowIfArgumentIsNegative(waitTime); Guard.ThrowIfArgumentIsNegative(pollInterval); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found .", waitTime); - if (success) + if (assertionChain.Succeeded) { return AssertionTaskAsync(); @@ -160,7 +165,7 @@ async Task, TResult>> try { TResult result = await Subject.Invoke(); - return new AndWhichConstraint, TResult>(this, result); + return new AndWhichConstraint, TResult>(this, result, assertionChain, ".Result"); } catch (Exception ex) { @@ -170,7 +175,7 @@ async Task, TResult>> } } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); diff --git a/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs index 91f2b85022..8c672ff0d2 100644 --- a/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs @@ -12,20 +12,24 @@ namespace FluentAssertions.Specialized; /// public class NonGenericAsyncFunctionAssertions : AsyncFunctionAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor) - : this(subject, extractor, new Clock()) + public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor, AssertionChain assertionChain) + : this(subject, extractor, assertionChain, new Clock()) { + this.assertionChain = assertionChain; } /// /// Initializes a new instance of the class with custom . /// - public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) - : base(subject, extractor, clock) + public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor, AssertionChain assertionChain, IClock clock) + : base(subject, extractor, assertionChain, clock) { + this.assertionChain = assertionChain; } /// @@ -42,25 +46,25 @@ public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions public async Task> CompleteWithinAsync( TimeSpan timeSpan, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}, but found .", timeSpan); - if (success) + if (assertionChain.Succeeded) { (Task task, TimeSpan remainingTime) = InvokeWithTimer(timeSpan); - success = Execute.Assertion + assertionChain .ForCondition(remainingTime >= TimeSpan.Zero) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); - if (success) + if (assertionChain.Succeeded) { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(task, remainingTime); - Execute.Assertion + assertionChain .ForCondition(completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); @@ -83,12 +87,12 @@ public async Task> CompleteWith public async Task> NotThrowAsync( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw{reason}, but found ."); - if (success) + if (assertionChain.Succeeded) { try { @@ -132,12 +136,12 @@ public Task> NotThrowAfterAsync Guard.ThrowIfArgumentIsNegative(waitTime); Guard.ThrowIfArgumentIsNegative(pollInterval); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found .", waitTime); - if (success) + if (assertionChain.Succeeded) { return AssertionTaskAsync(); @@ -160,7 +164,7 @@ async Task> AssertionTaskAsync( invocationEndTime = timer.Elapsed; } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); diff --git a/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs b/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs index a79d9d5cf2..0081053767 100644 --- a/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs +++ b/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs @@ -12,17 +12,20 @@ namespace FluentAssertions.Specialized; #if NET6_0_OR_GREATER public class TaskCompletionSourceAssertions : TaskCompletionSourceAssertionsBase { + private readonly AssertionChain assertionChain; private readonly TaskCompletionSource subject; - public TaskCompletionSourceAssertions(TaskCompletionSource tcs) - : this(tcs, new Clock()) + public TaskCompletionSourceAssertions(TaskCompletionSource tcs, AssertionChain assertionChain) + : this(tcs, assertionChain, new Clock()) { + this.assertionChain = assertionChain; } - public TaskCompletionSourceAssertions(TaskCompletionSource tcs, IClock clock) + public TaskCompletionSourceAssertions(TaskCompletionSource tcs, AssertionChain assertionChain, IClock clock) : base(clock) { subject = tcs; + this.assertionChain = assertionChain; } /// @@ -39,15 +42,15 @@ public TaskCompletionSourceAssertions(TaskCompletionSource tcs, IClock clock) public async Task> CompleteWithinAsync( TimeSpan timeSpan, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to complete within {0}{reason}, but found .", timeSpan); - if (success) + if (assertionChain.Succeeded) { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject!.Task, timeSpan); - Execute.Assertion + assertionChain .ForCondition(completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); @@ -70,15 +73,15 @@ public async Task> CompleteWithinA public async Task> NotCompleteWithinAsync( TimeSpan timeSpan, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to not complete within {0}{reason}, but found .", timeSpan); - if (success) + if (assertionChain.Succeeded) { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject!.Task, timeSpan); - Execute.Assertion + assertionChain .ForCondition(!completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to not complete within {0}{reason}.", timeSpan); @@ -91,17 +94,20 @@ public async Task> NotCompleteWith public class TaskCompletionSourceAssertions : TaskCompletionSourceAssertionsBase { + private readonly AssertionChain assertionChain; private readonly TaskCompletionSource subject; - public TaskCompletionSourceAssertions(TaskCompletionSource tcs) - : this(tcs, new Clock()) + public TaskCompletionSourceAssertions(TaskCompletionSource tcs, AssertionChain assertionChain) + : this(tcs, assertionChain, new Clock()) { + this.assertionChain = assertionChain; } - public TaskCompletionSourceAssertions(TaskCompletionSource tcs, IClock clock) + public TaskCompletionSourceAssertions(TaskCompletionSource tcs, AssertionChain assertionChain, IClock clock) : base(clock) { subject = tcs; + this.assertionChain = assertionChain; } /// @@ -118,16 +124,16 @@ public TaskCompletionSourceAssertions(TaskCompletionSource tcs, IClock clock) public async Task, T>> CompleteWithinAsync( TimeSpan timeSpan, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to complete within {0}{reason}, but found .", timeSpan); - if (success) + if (assertionChain.Succeeded) { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject!.Task, timeSpan); - Execute.Assertion + assertionChain .ForCondition(completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); @@ -135,7 +141,7 @@ public async Task, T>> Comp #pragma warning disable CA1849 // Call async methods when in an async method T result = subject.Task.IsCompleted ? subject.Task.Result : default; #pragma warning restore CA1849 // Call async methods when in an async method - return new AndWhichConstraint, T>(this, result); + return new AndWhichConstraint, T>(this, result, assertionChain, ".Result"); } return new AndWhichConstraint, T>(this, default(T)); @@ -155,16 +161,16 @@ public async Task, T>> Comp public async Task>> NotCompleteWithinAsync( TimeSpan timeSpan, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - var success = Execute.Assertion + assertionChain .ForCondition(subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context} to complete within {0}{reason}, but found .", timeSpan); - if (success) + if (assertionChain.Succeeded) { bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject!.Task, timeSpan); - Execute.Assertion + assertionChain .ForCondition(!completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:task} to complete within {0}{reason}.", timeSpan); diff --git a/Src/FluentAssertions/Streams/BufferedStreamAssertions.cs b/Src/FluentAssertions/Streams/BufferedStreamAssertions.cs index 70ce652d07..1f4a9dd604 100644 --- a/Src/FluentAssertions/Streams/BufferedStreamAssertions.cs +++ b/Src/FluentAssertions/Streams/BufferedStreamAssertions.cs @@ -1,9 +1,7 @@ -using System.Diagnostics; +#if NET6_0_OR_GREATER || NETSTANDARD2_1 +using System.Diagnostics; using System.IO; -#if NET6_0_OR_GREATER || NETSTANDARD2_1 -using System.Diagnostics.CodeAnalysis; using FluentAssertions.Execution; -#endif namespace FluentAssertions.Streams; @@ -14,8 +12,8 @@ namespace FluentAssertions.Streams; [DebuggerNonUserCode] public class BufferedStreamAssertions : BufferedStreamAssertions { - public BufferedStreamAssertions(BufferedStream stream) - : base(stream) + public BufferedStreamAssertions(BufferedStream stream, AssertionChain assertionChain) + : base(stream, assertionChain) { } } @@ -23,14 +21,16 @@ public BufferedStreamAssertions(BufferedStream stream) public class BufferedStreamAssertions : StreamAssertions where TAssertions : BufferedStreamAssertions { - public BufferedStreamAssertions(BufferedStream stream) - : base(stream) + private readonly AssertionChain assertionChain; + + public BufferedStreamAssertions(BufferedStream stream, AssertionChain assertionChain) + : base(stream, assertionChain) { + this.assertionChain = assertionChain; } protected override string Identifier => "buffered stream"; -#if NET6_0_OR_GREATER || NETSTANDARD2_1 /// /// Asserts that the current has the buffer size. /// @@ -42,18 +42,17 @@ public BufferedStreamAssertions(BufferedStream stream) /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint HaveBufferSize(int expected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint HaveBufferSize(int expected, string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the buffer size of {context:stream} to be {0}{reason}, but found a reference.", expected); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.BufferSize == expected) .FailWith("Expected the buffer size of {context:stream} to be {0}{reason}, but it was {1}.", @@ -74,18 +73,17 @@ public AndConstraint HaveBufferSize(int expected, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotHaveBufferSize(int unexpected, - [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotHaveBufferSize(int unexpected, string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the buffer size of {context:stream} not to be {0}{reason}, but found a reference.", unexpected); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.BufferSize != unexpected) .FailWith("Expected the buffer size of {context:stream} not to be {0}{reason}, but it was.", @@ -94,5 +92,5 @@ public AndConstraint NotHaveBufferSize(int unexpected, return new AndConstraint((TAssertions)this); } -#endif } +#endif diff --git a/Src/FluentAssertions/Streams/StreamAssertions.cs b/Src/FluentAssertions/Streams/StreamAssertions.cs index 7b61dd41f8..3116721541 100644 --- a/Src/FluentAssertions/Streams/StreamAssertions.cs +++ b/Src/FluentAssertions/Streams/StreamAssertions.cs @@ -13,8 +13,8 @@ namespace FluentAssertions.Streams; [DebuggerNonUserCode] public class StreamAssertions : StreamAssertions { - public StreamAssertions(Stream stream) - : base(stream) + public StreamAssertions(Stream stream, AssertionChain assertionChain) + : base(stream, assertionChain) { } } @@ -26,9 +26,12 @@ public class StreamAssertions : ReferenceTypeAssertions { - public StreamAssertions(TSubject stream) - : base(stream) + private readonly AssertionChain assertionChain; + + public StreamAssertions(TSubject stream, AssertionChain assertionChain) + : base(stream, assertionChain) { + this.assertionChain = assertionChain; } protected override string Identifier => "stream"; @@ -45,14 +48,14 @@ public StreamAssertions(TSubject stream) /// public AndConstraint BeWritable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} to be writable{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.CanWrite) .FailWith("Expected {context:stream} to be writable{reason}, but it was not."); @@ -73,14 +76,14 @@ public AndConstraint BeWritable([StringSyntax("CompositeFormat")] s /// public AndConstraint NotBeWritable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} not to be writable{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject!.CanWrite) .FailWith("Expected {context:stream} not to be writable{reason}, but it was."); @@ -101,14 +104,14 @@ public AndConstraint NotBeWritable([StringSyntax("CompositeFormat") /// public AndConstraint BeSeekable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} to be seekable{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.CanSeek) .FailWith("Expected {context:stream} to be seekable{reason}, but it was not."); @@ -129,14 +132,14 @@ public AndConstraint BeSeekable([StringSyntax("CompositeFormat")] s /// public AndConstraint NotBeSeekable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} not to be seekable{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject!.CanSeek) .FailWith("Expected {context:stream} not to be seekable{reason}, but it was."); @@ -157,14 +160,14 @@ public AndConstraint NotBeSeekable([StringSyntax("CompositeFormat") /// public AndConstraint BeReadable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} to be readable{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.CanRead) .FailWith("Expected {context:stream} to be readable{reason}, but it was not."); @@ -185,14 +188,14 @@ public AndConstraint BeReadable([StringSyntax("CompositeFormat")] s /// public AndConstraint NotBeReadable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} not to be readable{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject!.CanRead) .FailWith("Expected {context:stream} not to be readable{reason}, but it was."); @@ -215,13 +218,13 @@ public AndConstraint NotBeReadable([StringSyntax("CompositeFormat") public AndConstraint HavePosition(long expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the position of {context:stream} to be {0}{reason}, but found a reference.", expected); - if (success) + if (assertionChain.Succeeded) { long position; @@ -232,7 +235,7 @@ public AndConstraint HavePosition(long expected, catch (Exception exception) when (exception is IOException or NotSupportedException or ObjectDisposedException) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected the position of {context:stream} to be {0}{reason}, but it failed with:" + Environment.NewLine + "{1}", @@ -241,7 +244,7 @@ public AndConstraint HavePosition(long expected, return new AndConstraint((TAssertions)this); } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(position == expected) .FailWith("Expected the position of {context:stream} to be {0}{reason}, but it was {1}.", @@ -265,13 +268,13 @@ public AndConstraint HavePosition(long expected, public AndConstraint NotHavePosition(long unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the position of {context:stream} not to be {0}{reason}, but found a reference.", unexpected); - if (success) + if (assertionChain.Succeeded) { long position; @@ -282,7 +285,7 @@ public AndConstraint NotHavePosition(long unexpected, catch (Exception exception) when (exception is IOException or NotSupportedException or ObjectDisposedException) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected the position of {context:stream} not to be {0}{reason}, but it failed with:" + Environment.NewLine + "{1}", @@ -291,7 +294,7 @@ public AndConstraint NotHavePosition(long unexpected, return new AndConstraint((TAssertions)this); } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(position != unexpected) .FailWith("Expected the position of {context:stream} not to be {0}{reason}, but it was.", @@ -315,13 +318,13 @@ public AndConstraint NotHavePosition(long unexpected, public AndConstraint HaveLength(long expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the length of {context:stream} to be {0}{reason}, but found a reference.", expected); - if (success) + if (assertionChain.Succeeded) { long length; @@ -332,7 +335,7 @@ public AndConstraint HaveLength(long expected, catch (Exception exception) when (exception is IOException or NotSupportedException or ObjectDisposedException) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected the length of {context:stream} to be {0}{reason}, but it failed with:" + Environment.NewLine + "{1}", @@ -341,7 +344,7 @@ public AndConstraint HaveLength(long expected, return new AndConstraint((TAssertions)this); } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(length == expected) .FailWith("Expected the length of {context:stream} to be {0}{reason}, but it was {1}.", @@ -365,13 +368,13 @@ public AndConstraint HaveLength(long expected, public AndConstraint NotHaveLength(long unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the length of {context:stream} not to be {0}{reason}, but found a reference.", unexpected); - if (success) + if (assertionChain.Succeeded) { long length; @@ -382,7 +385,7 @@ public AndConstraint NotHaveLength(long unexpected, catch (Exception exception) when (exception is IOException or NotSupportedException or ObjectDisposedException) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected the length of {context:stream} not to be {0}{reason}, but it failed with:" + Environment.NewLine + "{1}", @@ -391,7 +394,7 @@ public AndConstraint NotHaveLength(long unexpected, return new AndConstraint((TAssertions)this); } - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(length != unexpected) .FailWith("Expected the length of {context:stream} not to be {0}{reason}, but it was.", @@ -413,14 +416,14 @@ public AndConstraint NotHaveLength(long unexpected, /// public AndConstraint BeReadOnly([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} to be read-only{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject!.CanWrite && Subject.CanRead) .FailWith("Expected {context:stream} to be read-only{reason}, but it was writable or not readable."); @@ -441,14 +444,14 @@ public AndConstraint BeReadOnly([StringSyntax("CompositeFormat")] s /// public AndConstraint NotBeReadOnly([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} not to be read-only{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.CanWrite || !Subject.CanRead) .FailWith("Expected {context:stream} not to be read-only{reason}, but it was."); @@ -469,14 +472,14 @@ public AndConstraint NotBeReadOnly([StringSyntax("CompositeFormat") /// public AndConstraint BeWriteOnly([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} to be write-only{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.CanWrite && !Subject.CanRead) .FailWith("Expected {context:stream} to be write-only{reason}, but it was readable or not writable."); @@ -497,14 +500,14 @@ public AndConstraint BeWriteOnly([StringSyntax("CompositeFormat")] /// public AndConstraint NotBeWriteOnly([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected {context:stream} not to be write-only{reason}, but found a reference."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject!.CanWrite || Subject.CanRead) .FailWith("Expected {context:stream} not to be write-only{reason}, but it was."); diff --git a/Src/FluentAssertions/Types/AssemblyAssertions.cs b/Src/FluentAssertions/Types/AssemblyAssertions.cs index 91e170fa67..5ceac78772 100644 --- a/Src/FluentAssertions/Types/AssemblyAssertions.cs +++ b/Src/FluentAssertions/Types/AssemblyAssertions.cs @@ -14,12 +14,15 @@ namespace FluentAssertions.Types; /// public class AssemblyAssertions : ReferenceTypeAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public AssemblyAssertions(Assembly assembly) - : base(assembly) + public AssemblyAssertions(Assembly assembly, AssertionChain assertionChain) + : base(assembly, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -41,19 +44,19 @@ public AndConstraint NotReference(Assembly assembly, var assemblyName = assembly.GetName().Name; - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected assembly not to reference assembly {0}{reason}, but {context:assembly} is .", assemblyName); - if (success) + if (assertionChain.Succeeded) { var subjectName = Subject!.GetName().Name; IEnumerable references = Subject.GetReferencedAssemblies().Select(x => x.Name); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!references.Contains(assemblyName)) .FailWith("Expected assembly {0} not to reference assembly {1}{reason}.", subjectName, assemblyName); @@ -81,18 +84,18 @@ public AndConstraint Reference(Assembly assembly, var assemblyName = assembly.GetName().Name; - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected assembly to reference assembly {0}{reason}, but {context:assembly} is .", assemblyName); - if (success) + if (assertionChain.Succeeded) { var subjectName = Subject!.GetName().Name; IEnumerable references = Subject.GetReferencedAssemblies().Select(x => x.Name); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(references.Contains(assemblyName)) .FailWith("Expected assembly {0} to reference assembly {1}{reason}, but it does not.", subjectName, assemblyName); @@ -120,7 +123,7 @@ public AndWhichConstraint DefineType(string @namespace { Guard.ThrowIfArgumentIsNullOrEmpty(name); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected assembly to define type {0}.{1}{reason}, but {context:assembly} is .", @@ -128,11 +131,11 @@ public AndWhichConstraint DefineType(string @namespace Type foundType = null; - if (success) + if (assertionChain.Succeeded) { foundType = Subject!.GetTypes().SingleOrDefault(t => t.Namespace == @namespace && t.Name == name); - Execute.Assertion + assertionChain .ForCondition(foundType is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected assembly {0} to define type {1}.{2}{reason}, but it does not.", @@ -150,15 +153,16 @@ public AndWhichConstraint DefineType(string @namespace /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint BeUnsigned([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeUnsigned([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .FailWith("Can't check for assembly signing if {context:assembly} reference is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject!.GetName().GetPublicKey() is not { Length: > 0 }) .FailWith( @@ -181,29 +185,28 @@ public AndConstraint BeUnsigned([StringSyntax("CompositeForm /// /// is . /// is empty. - public AndConstraint BeSignedWithPublicKey(string publicKey, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeSignedWithPublicKey(string publicKey, + [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { Guard.ThrowIfArgumentIsNullOrEmpty(publicKey); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .FailWith("Can't check for assembly signing if {context:assembly} reference is ."); - if (success) + if (assertionChain.Succeeded) { var bytes = Subject!.GetName().GetPublicKey() ?? []; string assemblyKey = ToHexString(bytes); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected assembly {0} to have public key {1} ", Subject.FullName, publicKey) - .ForCondition(bytes.Length != 0) - .FailWith("{reason}, but it is unsigned.") - .Then - .ForCondition(string.Equals(assemblyKey, publicKey, StringComparison.OrdinalIgnoreCase)) - .FailWith("{reason}, but it has {0} instead.", assemblyKey) - .Then - .ClearExpectation(); + .WithExpectation("Expected assembly {0} to have public key {1} ", Subject.FullName, publicKey, chain => chain + .ForCondition(bytes.Length != 0) + .FailWith("{reason}, but it is unsigned.") + .Then + .ForCondition(string.Equals(assemblyKey, publicKey, StringComparison.OrdinalIgnoreCase)) + .FailWith("{reason}, but it has {0} instead.", assemblyKey)); } return new AndConstraint(this); diff --git a/Src/FluentAssertions/Types/ConstructorInfoAssertions.cs b/Src/FluentAssertions/Types/ConstructorInfoAssertions.cs index cb66005a01..d3b1a3353e 100644 --- a/Src/FluentAssertions/Types/ConstructorInfoAssertions.cs +++ b/Src/FluentAssertions/Types/ConstructorInfoAssertions.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Reflection; +using FluentAssertions.Execution; namespace FluentAssertions.Types; @@ -13,8 +14,9 @@ public class ConstructorInfoAssertions : MethodBaseAssertions class. /// /// The constructorInfo from which to select properties. - public ConstructorInfoAssertions(ConstructorInfo constructorInfo) - : base(constructorInfo) + /// + public ConstructorInfoAssertions(ConstructorInfo constructorInfo, AssertionChain assertionChain) + : base(constructorInfo, assertionChain) { } @@ -23,7 +25,7 @@ internal static string GetDescriptionFor(ConstructorInfo constructorInfo) return $"{constructorInfo.DeclaringType}({GetParameterString(constructorInfo)})"; } - internal override string SubjectDescription => GetDescriptionFor(Subject); + protected override string SubjectDescription => GetDescriptionFor(Subject); protected override string Identifier => "constructor"; } diff --git a/Src/FluentAssertions/Types/MemberInfoAssertions.cs b/Src/FluentAssertions/Types/MemberInfoAssertions.cs index 082e9de153..7f447ecbd8 100644 --- a/Src/FluentAssertions/Types/MemberInfoAssertions.cs +++ b/Src/FluentAssertions/Types/MemberInfoAssertions.cs @@ -19,9 +19,12 @@ public abstract class MemberInfoAssertions : ReferenceTyp where TSubject : MemberInfo where TAssertions : MemberInfoAssertions { - protected MemberInfoAssertions(TSubject subject) - : base(subject) + private readonly AssertionChain assertionChain; + + protected MemberInfoAssertions(TSubject subject, AssertionChain assertionChain) + : base(subject, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -80,7 +83,7 @@ public AndWhichConstraint, TAttribut { Guard.ThrowIfArgumentIsNull(isMatchingAttributePredicate); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( @@ -89,11 +92,11 @@ public AndWhichConstraint, TAttribut IEnumerable attributes = []; - if (success) + if (assertionChain.Succeeded) { attributes = Subject.GetMatchingAttributes(isMatchingAttributePredicate); - Execute.Assertion + assertionChain .ForCondition(attributes.Any()) .BecauseOf(because, becauseArgs) .FailWith( @@ -126,18 +129,18 @@ public AndConstraint NotBeDecoratedWith( { Guard.ThrowIfArgumentIsNull(isMatchingAttributePredicate); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( $"Expected {Identifier} to not be decorated with {typeof(TAttribute)}{{reason}}" + ", but {context:member} is ."); - if (success) + if (assertionChain.Succeeded) { IEnumerable attributes = Subject.GetMatchingAttributes(isMatchingAttributePredicate); - Execute.Assertion + assertionChain .ForCondition(!attributes.Any()) .BecauseOf(because, becauseArgs) .FailWith( @@ -150,5 +153,5 @@ public AndConstraint NotBeDecoratedWith( protected override string Identifier => "member"; - internal virtual string SubjectDescription => $"{Subject.DeclaringType}.{Subject.Name}"; + protected virtual string SubjectDescription => $"{Subject.DeclaringType}.{Subject.Name}"; } diff --git a/Src/FluentAssertions/Types/MethodBaseAssertions.cs b/Src/FluentAssertions/Types/MethodBaseAssertions.cs index c45591c3c6..3b7c995b5d 100644 --- a/Src/FluentAssertions/Types/MethodBaseAssertions.cs +++ b/Src/FluentAssertions/Types/MethodBaseAssertions.cs @@ -17,9 +17,12 @@ public abstract class MethodBaseAssertions : MemberInfoAs where TSubject : MethodBase where TAssertions : MethodBaseAssertions { - protected MethodBaseAssertions(TSubject subject) - : base(subject) + private readonly AssertionChain assertionChain; + + protected MethodBaseAssertions(TSubject subject, AssertionChain assertionChain) + : base(subject, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -41,20 +44,23 @@ public AndConstraint HaveAccessModifier( { Guard.ThrowIfArgumentIsOutOfRange(accessModifier); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith($"Expected method to be {accessModifier}{{reason}}, but {{context:member}} is ."); + .FailWith($"Expected method to be {accessModifier}{{reason}}, but {{context:method}} is ."); - if (success) + if (assertionChain.Succeeded) { CSharpAccessModifier subjectAccessModifier = Subject.GetCSharpAccessModifier(); - Execute.Assertion + var subject = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "method " + Subject.ToFormattedString(); + + assertionChain .ForCondition(accessModifier == subjectAccessModifier) .BecauseOf(because, becauseArgs) - .FailWith( - $"Expected method {Subject!.Name} to be {accessModifier}{{reason}}, but it is {subjectAccessModifier}."); + .FailWith($"Expected {subject} to be {accessModifier}{{reason}}, but it is {subjectAccessModifier}."); } return new AndConstraint((TAssertions)this); @@ -78,19 +84,23 @@ public AndConstraint NotHaveAccessModifier(CSharpAccessModifier acc { Guard.ThrowIfArgumentIsOutOfRange(accessModifier); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith($"Expected method not to be {accessModifier}{{reason}}, but {{context:member}} is ."); - if (success) + if (assertionChain.Succeeded) { CSharpAccessModifier subjectAccessModifier = Subject.GetCSharpAccessModifier(); - Execute.Assertion + var subject = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "method " + Subject.ToFormattedString(); + + assertionChain .ForCondition(accessModifier != subjectAccessModifier) .BecauseOf(because, becauseArgs) - .FailWith($"Expected method {Subject!.Name} not to be {accessModifier}{{reason}}, but it is."); + .FailWith($"Expected {subject} not to be {accessModifier}{{reason}}, but it is."); } return new AndConstraint((TAssertions)this); diff --git a/Src/FluentAssertions/Types/MethodInfoAssertions.cs b/Src/FluentAssertions/Types/MethodInfoAssertions.cs index c4dff054eb..9aa4beef1e 100644 --- a/Src/FluentAssertions/Types/MethodInfoAssertions.cs +++ b/Src/FluentAssertions/Types/MethodInfoAssertions.cs @@ -13,9 +13,12 @@ namespace FluentAssertions.Types; [DebuggerNonUserCode] public class MethodInfoAssertions : MethodBaseAssertions { - public MethodInfoAssertions(MethodInfo methodInfo) - : base(methodInfo) + private readonly AssertionChain assertionChain; + + public MethodInfoAssertions(MethodInfo methodInfo, AssertionChain assertionChain) + : base(methodInfo, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -30,14 +33,14 @@ public MethodInfoAssertions(MethodInfo methodInfo) /// public AndConstraint BeVirtual([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected method to be virtual{reason}, but {context:member} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(!Subject.IsNonVirtual()) .BecauseOf(because, becauseArgs) .FailWith("Expected method " + SubjectDescription + " to be virtual{reason}, but it is not virtual."); @@ -58,14 +61,14 @@ public AndConstraint BeVirtual([StringSyntax("CompositeFor /// public AndConstraint NotBeVirtual([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected method not to be virtual{reason}, but {context:member} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject.IsNonVirtual()) .BecauseOf(because, becauseArgs) .FailWith("Expected method " + SubjectDescription + " not to be virtual{reason}, but it is."); @@ -86,14 +89,14 @@ public AndConstraint NotBeVirtual([StringSyntax("Composite /// public AndConstraint BeAsync([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected method to be async{reason}, but {context:member} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject.IsAsync()) .BecauseOf(because, becauseArgs) .FailWith("Expected method " + SubjectDescription + " to be async{reason}, but it is not."); @@ -114,14 +117,14 @@ public AndConstraint BeAsync([StringSyntax("CompositeForma /// public AndConstraint NotBeAsync([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected method not to be async{reason}, but {context:member} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(!Subject.IsAsync()) .BecauseOf(because, becauseArgs) .FailWith("Expected method " + SubjectDescription + " not to be async{reason}, but it is."); @@ -143,14 +146,14 @@ public AndConstraint NotBeAsync([StringSyntax("CompositeFo public AndConstraint> ReturnVoid( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the return type of method to be void{reason}, but {context:member} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(typeof(void) == Subject!.ReturnType) .BecauseOf(because, becauseArgs) .FailWith("Expected the return type of method " + Subject.Name + " to be void{reason}, but it is {0}.", @@ -177,14 +180,14 @@ public AndConstraint> Ret { Guard.ThrowIfArgumentIsNull(returnType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the return type of method to be {0}{reason}, but {context:member} is .", returnType); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(returnType == Subject!.ReturnType) .BecauseOf(because, becauseArgs) .FailWith("Expected the return type of method " + Subject.Name + " to be {0}{reason}, but it is {1}.", @@ -224,14 +227,14 @@ public AndConstraint> Ret public AndConstraint> NotReturnVoid( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the return type of method not to be void{reason}, but {context:member} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(typeof(void) != Subject!.ReturnType) .BecauseOf(because, becauseArgs) .FailWith("Expected the return type of method " + Subject.Name + " not to be void{reason}, but it is."); @@ -257,15 +260,15 @@ public AndConstraint> Not { Guard.ThrowIfArgumentIsNull(returnType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( "Expected the return type of method not to be {0}{reason}, but {context:member} is .", returnType); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(returnType != Subject!.ReturnType) .BecauseOf(because, becauseArgs) .FailWith( @@ -304,7 +307,7 @@ internal static string GetDescriptionFor(MethodInfo method) return $"{returnTypeName} {method.DeclaringType}.{method.Name}"; } - internal override string SubjectDescription => GetDescriptionFor(Subject); + protected override string SubjectDescription => GetDescriptionFor(Subject); protected override string Identifier => "method"; } diff --git a/Src/FluentAssertions/Types/MethodInfoSelectorAssertions.cs b/Src/FluentAssertions/Types/MethodInfoSelectorAssertions.cs index dfc2effa49..71b3c51213 100644 --- a/Src/FluentAssertions/Types/MethodInfoSelectorAssertions.cs +++ b/Src/FluentAssertions/Types/MethodInfoSelectorAssertions.cs @@ -18,13 +18,16 @@ namespace FluentAssertions.Types; [DebuggerNonUserCode] public class MethodInfoSelectorAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// /// The methods to assert. /// is . - public MethodInfoSelectorAssertions(params MethodInfo[] methods) + public MethodInfoSelectorAssertions(AssertionChain assertionChain, params MethodInfo[] methods) { + this.assertionChain = assertionChain; Guard.ThrowIfArgumentIsNull(methods); SubjectMethods = methods; @@ -54,7 +57,7 @@ public AndConstraint BeVirtual([StringSyntax("Comp Environment.NewLine + GetDescriptionsFor(nonVirtualMethods); - Execute.Assertion + assertionChain .ForCondition(nonVirtualMethods.Length == 0) .BecauseOf(because, becauseArgs) .FailWith(failureMessage); @@ -81,7 +84,7 @@ public AndConstraint NotBeVirtual([StringSyntax("C Environment.NewLine + GetDescriptionsFor(virtualMethods); - Execute.Assertion + assertionChain .ForCondition(virtualMethods.Length == 0) .BecauseOf(because, becauseArgs) .FailWith(failureMessage); @@ -118,7 +121,7 @@ public AndConstraint BeAsync([StringSyntax("Compos Environment.NewLine + GetDescriptionsFor(nonAsyncMethods); - Execute.Assertion + assertionChain .ForCondition(nonAsyncMethods.Length == 0) .BecauseOf(because, becauseArgs) .FailWith(failureMessage); @@ -145,7 +148,7 @@ public AndConstraint NotBeAsync([StringSyntax("Com Environment.NewLine + GetDescriptionsFor(asyncMethods); - Execute.Assertion + assertionChain .ForCondition(asyncMethods.Length == 0) .BecauseOf(because, becauseArgs) .FailWith(failureMessage); @@ -199,7 +202,7 @@ public AndConstraint BeDecoratedWith( Environment.NewLine + GetDescriptionsFor(methodsWithoutAttribute); - Execute.Assertion + assertionChain .ForCondition(methodsWithoutAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith(failureMessage, typeof(TAttribute)); @@ -253,7 +256,7 @@ public AndConstraint NotBeDecoratedWith Be(CSharpAccessModifier acces var message = $"Expected all selected methods to be {accessModifier}{{reason}}, but the following methods are not:" + Environment.NewLine + GetDescriptionsFor(methods); - Execute.Assertion + assertionChain .ForCondition(methods.Length == 0) .BecauseOf(because, becauseArgs) .FailWith(message); @@ -307,7 +310,7 @@ public AndConstraint NotBe(CSharpAccessModifier ac var message = $"Expected all selected methods to not be {accessModifier}{{reason}}, but the following methods are:" + Environment.NewLine + GetDescriptionsFor(methods); - Execute.Assertion + assertionChain .ForCondition(methods.Length == 0) .BecauseOf(because, becauseArgs) .FailWith(message); diff --git a/Src/FluentAssertions/Types/PropertyInfoAssertions.cs b/Src/FluentAssertions/Types/PropertyInfoAssertions.cs index 0201ccde6b..c5b4fd605e 100644 --- a/Src/FluentAssertions/Types/PropertyInfoAssertions.cs +++ b/Src/FluentAssertions/Types/PropertyInfoAssertions.cs @@ -1,9 +1,11 @@ using System; +using System.Data.Common; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using FluentAssertions.Common; using FluentAssertions.Execution; +using FluentAssertions.Formatting; namespace FluentAssertions.Types; @@ -13,9 +15,12 @@ namespace FluentAssertions.Types; [DebuggerNonUserCode] public class PropertyInfoAssertions : MemberInfoAssertions { - public PropertyInfoAssertions(PropertyInfo propertyInfo) - : base(propertyInfo) + private readonly AssertionChain assertionChain; + + public PropertyInfoAssertions(PropertyInfo propertyInfo, AssertionChain assertionChain) + : base(propertyInfo, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -31,18 +36,18 @@ public PropertyInfoAssertions(PropertyInfo propertyInfo) public AndConstraint BeVirtual( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith("Expected property to be virtual{reason}, but {context:property} is ."); - - if (success) - { - Execute.Assertion - .ForCondition(Subject.IsVirtual()) - .BecauseOf(because, becauseArgs) - .FailWith($"Expected property {GetDescriptionFor(Subject)} to be virtual{{reason}}, but it is not."); - } + .FailWith("Expected property to be virtual{reason}, but {context:property} is .") + .Then + .ForCondition(Subject.IsVirtual()) + .BecauseOf(because, becauseArgs) + .FailWith($"Expected {subjectDescription} to be virtual{{reason}}, but it is not."); return new AndConstraint(this); } @@ -57,20 +62,21 @@ public AndConstraint BeVirtual( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeVirtual([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeVirtual([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith("Expected property not to be virtual{reason}, but {context:property} is ."); - - if (success) - { - Execute.Assertion - .ForCondition(!Subject.IsVirtual()) - .BecauseOf(because, becauseArgs) - .FailWith($"Expected property {GetDescriptionFor(Subject)} not to be virtual{{reason}}, but it is."); - } + .FailWith("Expected property not to be virtual{reason}, but {context:property} is .") + .Then + .ForCondition(!Subject.IsVirtual()) + .BecauseOf(because, becauseArgs) + .FailWith($"Expected property {subjectDescription} not to be virtual{{reason}}, but it is."); return new AndConstraint(this); } @@ -88,20 +94,20 @@ public AndConstraint NotBeVirtual([StringSyntax("Composi public AndConstraint BeWritable( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith("Expected property to have a setter{reason}, but {context:property} is ."); - - if (success) - { - Execute.Assertion - .ForCondition(Subject!.CanWrite) - .BecauseOf(because, becauseArgs) - .FailWith( - "Expected {context:property} {0} to have a setter{reason}.", - Subject); - } + .FailWith("Expected property to have a setter{reason}, but {context:property} is .") + .Then + .ForCondition(Subject!.CanWrite) + .BecauseOf(because, becauseArgs) + .FailWith( + $"Expected {subjectDescription} to have a setter{{reason}}.", + Subject); return new AndConstraint(this); } @@ -124,24 +130,25 @@ public AndConstraint BeWritable(CSharpAccessModifier acc { Guard.ThrowIfArgumentIsOutOfRange(accessModifier); - bool success = Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith($"Expected {Identifier} to be {accessModifier}{{reason}}, but {{context:property}} is ."); + .FailWith($"Expected {{context:project}} to be {accessModifier}{{reason}}, but it is .") + .Then + .ForCondition(Subject!.CanWrite) + .BecauseOf(because, becauseArgs) + .FailWith($"Expected {subjectDescription} to have a setter{{reason}}."); - if (success) + if (assertionChain.Succeeded) { - success = Execute.Assertion - .ForCondition(Subject!.CanWrite) - .BecauseOf(because, becauseArgs) - .FailWith( - "Expected {context:property} {0} to have a setter{reason}.", - Subject); + assertionChain.OverrideCallerIdentifier(() => "setter of " + subjectDescription); + assertionChain.ReuseOnce(); - if (success) - { - Subject!.GetSetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); - } + Subject!.GetSetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); } return new AndConstraint(this); @@ -160,20 +167,18 @@ public AndConstraint BeWritable(CSharpAccessModifier acc public AndConstraint NotBeWritable( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith("Expected property not to have a setter{reason}, but {context:property} is ."); - - if (success) - { - Execute.Assertion - .ForCondition(!Subject!.CanWrite) - .BecauseOf(because, becauseArgs) - .FailWith( - "Expected {context:property} {0} not to have a setter{reason}.", - Subject); - } + .FailWith("Expected {context:property} not to have a setter{reason}, but it is .") + .Then + .ForCondition(!Subject!.CanWrite) + .BecauseOf(because, becauseArgs) + .FailWith($"Did not expect {subjectDescription} to have a setter{{reason}}."); return new AndConstraint(this); } @@ -188,18 +193,23 @@ public AndConstraint NotBeWritable( /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint BeReadable([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeReadable([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected property to have a getter{reason}, but {context:property} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion.ForCondition(Subject!.CanRead) + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain.ForCondition(Subject!.CanRead) .BecauseOf(because, becauseArgs) - .FailWith("Expected property " + Subject.Name + " to have a getter{reason}, but it does not."); + .FailWith($"Expected property {subjectDescription} to have a getter{{reason}}, but it does not."); } return new AndConstraint(this); @@ -223,21 +233,25 @@ public AndConstraint BeReadable(CSharpAccessModifier acc { Guard.ThrowIfArgumentIsOutOfRange(accessModifier); - bool success = Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith($"Expected {Identifier} to be {accessModifier}{{reason}}, but {{context:property}} is ."); + .FailWith($"Expected {{context:property}} to be {accessModifier}{{reason}}, but it is .") + .Then + .ForCondition(Subject!.CanRead) + .BecauseOf(because, becauseArgs) + .FailWith($"Expected {subjectDescription} to have a getter{{reason}}, but it does not."); - if (success) + if (assertionChain.Succeeded) { - success = Execute.Assertion.ForCondition(Subject!.CanRead) - .BecauseOf(because, becauseArgs) - .FailWith("Expected property " + Subject.Name + " to have a getter{reason}, but it does not."); + assertionChain.OverrideCallerIdentifier(() => "getter of " + subjectDescription); + assertionChain.ReuseOnce(); - if (success) - { - Subject!.GetGetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); - } + Subject!.GetGetMethod(nonPublic: true).Should().HaveAccessModifier(accessModifier, because, becauseArgs); } return new AndConstraint(this); @@ -256,19 +270,22 @@ public AndConstraint BeReadable(CSharpAccessModifier acc public AndConstraint NotBeReadable( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected property not to have a getter{reason}, but {context:property} is ."); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier + ? assertionChain.CallerIdentifier + : "property " + Subject.ToFormattedString(); + + assertionChain .ForCondition(!Subject!.CanRead) .BecauseOf(because, becauseArgs) .FailWith( - "Expected {context:property} {0} not to have a getter{reason}.", - Subject); + $"Did not expect {subjectDescription} to have a getter{{reason}}.", Subject); } return new AndConstraint(this); @@ -291,17 +308,17 @@ public AndConstraint Return(Type propertyType, { Guard.ThrowIfArgumentIsNull(propertyType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type of property to be {0}{reason}, but {context:property} is .", propertyType); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion.ForCondition(Subject!.PropertyType == propertyType) + assertionChain.ForCondition(Subject!.PropertyType == propertyType) .BecauseOf(because, becauseArgs) - .FailWith("Expected Type of property " + Subject.Name + " to be {0}{reason}, but it is {1}.", - propertyType, Subject.PropertyType); + .FailWith("Expected type of property {2} to be {0}{reason}, but it is {1}.", + propertyType, Subject.PropertyType, Subject); } return new AndConstraint(this); @@ -318,7 +335,8 @@ public AndConstraint Return(Type propertyType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Return([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Return([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return Return(typeof(TReturn), because, becauseArgs); } @@ -340,17 +358,17 @@ public AndConstraint NotReturn(Type propertyType, { Guard.ThrowIfArgumentIsNull(propertyType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type of property not to be {0}{reason}, but {context:property} is .", propertyType); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject!.PropertyType != propertyType) .BecauseOf(because, becauseArgs) - .FailWith("Expected Type of property " + Subject.Name + " not to be {0}{reason}, but it is.", propertyType); + .FailWith("Expected type of property {1} not to be {0}{reason}, but it is.", propertyType, Subject); } return new AndConstraint(this); @@ -367,24 +385,13 @@ public AndConstraint NotReturn(Type propertyType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotReturn([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotReturn([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotReturn(typeof(TReturn), because, becauseArgs); } - internal static string GetDescriptionFor(PropertyInfo property) - { - if (property is null) - { - return string.Empty; - } - - var propTypeName = property.PropertyType.Name; - - return $"{propTypeName} {property.DeclaringType}.{property.Name}"; - } - - internal override string SubjectDescription => GetDescriptionFor(Subject); + protected override string SubjectDescription => Formatter.ToString(Subject); /// /// Returns the type of the subject the assertion applies on. diff --git a/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs b/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs index 6605f823e9..4aca174681 100644 --- a/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs +++ b/Src/FluentAssertions/Types/PropertyInfoSelectorAssertions.cs @@ -6,6 +6,7 @@ using System.Reflection; using FluentAssertions.Common; using FluentAssertions.Execution; +using FluentAssertions.Formatting; namespace FluentAssertions.Types; @@ -17,6 +18,8 @@ namespace FluentAssertions.Types; [DebuggerNonUserCode] public class PropertyInfoSelectorAssertions { + private readonly AssertionChain assertionChain; + /// /// Gets the object whose value is being asserted. /// @@ -27,8 +30,9 @@ public class PropertyInfoSelectorAssertions /// /// The properties to assert. /// is . - public PropertyInfoSelectorAssertions(params PropertyInfo[] properties) + public PropertyInfoSelectorAssertions(AssertionChain assertionChain, params PropertyInfo[] properties) { + this.assertionChain = assertionChain; Guard.ThrowIfArgumentIsNull(properties); SubjectProperties = properties; @@ -48,7 +52,7 @@ public AndConstraint BeVirtual([StringSyntax("Co { PropertyInfo[] nonVirtualProperties = GetAllNonVirtualPropertiesFromSelection(); - Execute.Assertion + assertionChain .ForCondition(nonVirtualProperties.Length == 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -72,7 +76,7 @@ public AndConstraint NotBeVirtual([StringSyntax( { PropertyInfo[] virtualProperties = GetAllVirtualPropertiesFromSelection(); - Execute.Assertion + assertionChain .ForCondition(virtualProperties.Length == 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -96,7 +100,7 @@ public AndConstraint BeWritable([StringSyntax("C { PropertyInfo[] readOnlyProperties = GetAllReadOnlyPropertiesFromSelection(); - Execute.Assertion + assertionChain .ForCondition(readOnlyProperties.Length == 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -120,7 +124,7 @@ public AndConstraint NotBeWritable([StringSyntax { PropertyInfo[] writableProperties = GetAllWritablePropertiesFromSelection(); - Execute.Assertion + assertionChain .ForCondition(writableProperties.Length == 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -166,7 +170,7 @@ public AndConstraint BeDecoratedWith { PropertyInfo[] propertiesWithoutAttribute = GetPropertiesWithout(); - Execute.Assertion + assertionChain .ForCondition(propertiesWithoutAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -193,7 +197,7 @@ public AndConstraint NotBeDecoratedWith(); - Execute.Assertion + assertionChain .ForCondition(propertiesWithAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith( @@ -218,7 +222,7 @@ private PropertyInfo[] GetPropertiesWith() private static string GetDescriptionsFor(IEnumerable properties) { - IEnumerable descriptions = properties.Select(property => PropertyInfoAssertions.GetDescriptionFor(property)); + IEnumerable descriptions = properties.Select(property => Formatter.ToString(property)); return string.Join(Environment.NewLine, descriptions); } diff --git a/Src/FluentAssertions/Types/TypeAssertions.cs b/Src/FluentAssertions/Types/TypeAssertions.cs index 45cf1618cc..f16fadc14a 100644 --- a/Src/FluentAssertions/Types/TypeAssertions.cs +++ b/Src/FluentAssertions/Types/TypeAssertions.cs @@ -17,12 +17,15 @@ namespace FluentAssertions.Types; [DebuggerNonUserCode] public class TypeAssertions : ReferenceTypeAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public TypeAssertions(Type type) - : base(type) + public TypeAssertions(Type type, AssertionChain assertionChain) + : base(type, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -35,7 +38,8 @@ public TypeAssertions(Type type) /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Be([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Be([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return Be(typeof(TExpected), because, becauseArgs); } @@ -54,7 +58,7 @@ public AndConstraint Be([StringSyntax("CompositeForma public AndConstraint Be(Type expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject == expected) .FailWith(GetFailureMessageIfTypesAreDifferent(Subject, expected)); @@ -74,7 +78,8 @@ public AndConstraint Be(Type expected, /// Zero or more objects to format using the placeholders in . /// /// An which can be used to chain assertions. - public new AndConstraint BeAssignableTo([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public new AndConstraint BeAssignableTo([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return BeAssignableTo(typeof(T), because, becauseArgs); } @@ -101,7 +106,7 @@ public AndConstraint Be(Type expected, ? Subject.IsAssignableToOpenGeneric(type) : type.IsAssignableFrom(Subject); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(isAssignable) .FailWith("Expected {context:type} {0} to be assignable to {1}{reason}, but it is not.", Subject, type); @@ -121,7 +126,8 @@ public AndConstraint Be(Type expected, /// Zero or more objects to format using the placeholders in . /// /// An which can be used to chain assertions. - public new AndConstraint NotBeAssignableTo([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public new AndConstraint NotBeAssignableTo([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotBeAssignableTo(typeof(T), because, becauseArgs); } @@ -148,7 +154,7 @@ public AndConstraint Be(Type expected, ? Subject.IsAssignableToOpenGeneric(type) : type.IsAssignableFrom(Subject); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!isAssignable) .FailWith("Expected {context:type} {0} to not be assignable to {1}{reason}, but it is.", Subject, type); @@ -193,7 +199,8 @@ private static string GetFailureMessageIfTypesAreDifferent(Type actual, Type exp /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBe([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBe([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { return NotBe(typeof(TUnexpected), because, becauseArgs); } @@ -214,7 +221,7 @@ public AndConstraint NotBe(Type unexpected, { string nameOfUnexpectedType = unexpected is not null ? $"[{unexpected.AssemblyQualifiedName}]" : ""; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject != unexpected) .FailWith("Expected type not to be " + nameOfUnexpectedType + "{reason}, but it is."); @@ -238,7 +245,7 @@ public AndWhichConstraint BeDecoratedWith attributes = Subject.GetMatchingAttributes(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(attributes.Any()) .FailWith("Expected type {0} to be decorated with {1}{reason}, but the attribute was not found.", @@ -273,7 +280,7 @@ public AndWhichConstraint BeDecoratedWith attributes = Subject.GetMatchingAttributes(isMatchingAttributePredicate); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(attributes.Any()) .FailWith( @@ -299,7 +306,7 @@ public AndWhichConstraint BeDecoratedWithOrInherit attributes = Subject.GetMatchingOrInheritedAttributes(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(attributes.Any()) .FailWith("Expected type {0} to be decorated with or inherit {1}{reason}, but the attribute was not found.", @@ -334,7 +341,7 @@ public AndWhichConstraint BeDecoratedWithOrInherit attributes = Subject.GetMatchingOrInheritedAttributes(isMatchingAttributePredicate); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(attributes.Any()) .FailWith( @@ -354,10 +361,11 @@ public AndWhichConstraint BeDecoratedWithOrInherit /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeDecoratedWith([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeDecoratedWith([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) where TAttribute : Attribute { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.IsDecoratedWith()) .FailWith("Expected type {0} to not be decorated with {1}{reason}, but the attribute was found.", @@ -388,7 +396,7 @@ public AndConstraint NotBeDecoratedWith( { Guard.ThrowIfArgumentIsNull(isMatchingAttributePredicate); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.IsDecoratedWith(isMatchingAttributePredicate)) .FailWith( @@ -413,7 +421,7 @@ public AndConstraint NotBeDecoratedWithOrInherit( [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) where TAttribute : Attribute { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.IsDecoratedWithOrInherit()) .FailWith("Expected type {0} to not be decorated with or inherit {1}{reason}, but the attribute was found.", @@ -445,7 +453,7 @@ public AndConstraint NotBeDecoratedWithOrInherit( { Guard.ThrowIfArgumentIsNull(isMatchingAttributePredicate); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.IsDecoratedWithOrInherit(isMatchingAttributePredicate)) .FailWith( @@ -482,16 +490,17 @@ private bool AssertSubjectImplements(Type interfaceType, { bool containsInterface = interfaceType.IsAssignableFrom(Subject) && interfaceType != Subject; - return Execute.Assertion - .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type {0} to implement interface {1}{reason}", Subject, interfaceType) - .ForCondition(interfaceType.IsInterface) - .FailWith(", but {0} is not an interface.", interfaceType) - .Then - .ForCondition(containsInterface) - .FailWith(", but it does not.") - .Then - .ClearExpectation(); + assertionChain + .BecauseOf(because, becauseArgs) + .WithExpectation("Expected type {0} to implement interface {1}{reason}", Subject, interfaceType, chain => chain + + .ForCondition(interfaceType.IsInterface) + .FailWith(", but {0} is not an interface.", interfaceType) + .Then + .ForCondition(containsInterface) + .FailWith(", but it does not.")); + + return assertionChain.Succeeded; } /// @@ -505,7 +514,8 @@ private bool AssertSubjectImplements(Type interfaceType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint Implement([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint Implement([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) where TInterface : class { return Implement(typeof(TInterface), because, becauseArgs); @@ -530,16 +540,15 @@ public AndConstraint NotImplement(Type interfaceType, bool containsInterface = interfaceType.IsAssignableFrom(Subject) && interfaceType != Subject; - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type {0} to not implement interface {1}{reason}", Subject, interfaceType) - .ForCondition(interfaceType.IsInterface) + .WithExpectation("Expected type {0} to not implement interface {1}{reason}", Subject, interfaceType, chain => chain + + .ForCondition(interfaceType.IsInterface) .FailWith(", but {0} is not an interface.", interfaceType) .Then .ForCondition(!containsInterface) - .FailWith(", but it does.", interfaceType) - .Then - .ClearExpectation(); + .FailWith(", but it does.", interfaceType)); return new AndConstraint(this); } @@ -555,7 +564,8 @@ public AndConstraint NotImplement(Type interfaceType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotImplement([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotImplement([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) where TInterface : class { return NotImplement(typeof(TInterface), because, becauseArgs); @@ -582,16 +592,15 @@ public AndConstraint BeDerivedFrom(Type baseType, ? Subject.IsDerivedFromOpenGeneric(baseType) : Subject.IsSubclassOf(baseType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type {0} to be derived from {1}{reason}", Subject, baseType) - .ForCondition(!baseType.IsInterface) + .WithExpectation("Expected type {0} to be derived from {1}{reason}", Subject, baseType, chain => chain + + .ForCondition(!baseType.IsInterface) .FailWith(", but {0} is an interface.", baseType) .Then .ForCondition(isDerivedFrom) - .FailWith(", but it is not.") - .Then - .ClearExpectation(); + .FailWith(", but it is not.")); return new AndConstraint(this); } @@ -607,7 +616,8 @@ public AndConstraint BeDerivedFrom(Type baseType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint BeDerivedFrom([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeDerivedFrom([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) where TBaseClass : class { return BeDerivedFrom(typeof(TBaseClass), because, becauseArgs); @@ -634,16 +644,15 @@ public AndConstraint NotBeDerivedFrom(Type baseType, ? Subject.IsDerivedFromOpenGeneric(baseType) : Subject.IsSubclassOf(baseType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) - .WithExpectation("Expected type {0} not to be derived from {1}{reason}", Subject, baseType) - .ForCondition(!baseType.IsInterface) + .WithExpectation("Expected type {0} not to be derived from {1}{reason}", Subject, baseType, chain => chain + + .ForCondition(!baseType.IsInterface) .FailWith(", but {0} is an interface.", baseType) .Then .ForCondition(!isDerivedFrom) - .FailWith(", but it is.") - .Then - .ClearExpectation(); + .FailWith(", but it is.")); return new AndConstraint(this); } @@ -659,7 +668,8 @@ public AndConstraint NotBeDerivedFrom(Type baseType, /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint NotBeDerivedFrom([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeDerivedFrom([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) where TBaseClass : class { return NotBeDerivedFrom(typeof(TBaseClass), because, becauseArgs); @@ -677,18 +687,19 @@ public AndConstraint NotBeDerivedFrom([StringSyntax( /// /// /// is not a class. - public AndConstraint BeSealed([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeSealed([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type to be sealed{reason}, but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { AssertThatSubjectIsClass(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.IsCSharpSealed()) .FailWith("Expected type {0} to be sealed{reason}.", Subject); @@ -709,18 +720,19 @@ public AndConstraint BeSealed([StringSyntax("CompositeFormat")] /// /// /// is not a class. - public AndConstraint NotBeSealed([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeSealed([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type not to be sealed{reason}, but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { AssertThatSubjectIsClass(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.IsCSharpSealed()) .FailWith("Expected type {0} not to be sealed{reason}.", Subject); @@ -741,18 +753,19 @@ public AndConstraint NotBeSealed([StringSyntax("CompositeFormat" /// /// /// is not a class. - public AndConstraint BeAbstract([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeAbstract([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type to be abstract{reason}, but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { AssertThatSubjectIsClass(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.IsCSharpAbstract()) .FailWith("Expected {context:type} {0} to be abstract{reason}.", Subject); @@ -773,18 +786,19 @@ public AndConstraint BeAbstract([StringSyntax("CompositeFormat") /// /// /// is not a class. - public AndConstraint NotBeAbstract([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeAbstract([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type not to be abstract{reason}, but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { AssertThatSubjectIsClass(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.IsCSharpAbstract()) .FailWith("Expected type {0} not to be abstract{reason}.", Subject); @@ -805,18 +819,19 @@ public AndConstraint NotBeAbstract([StringSyntax("CompositeForma /// /// /// is not a class. - public AndConstraint BeStatic([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint BeStatic([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type to be static{reason}, but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { AssertThatSubjectIsClass(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject.IsCSharpStatic()) .FailWith("Expected type {0} to be static{reason}.", Subject); @@ -837,18 +852,19 @@ public AndConstraint BeStatic([StringSyntax("CompositeFormat")] /// /// /// is not a class. - public AndConstraint NotBeStatic([StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) + public AndConstraint NotBeStatic([StringSyntax("CompositeFormat")] string because = "", + params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected type not to be static{reason}, but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { AssertThatSubjectIsClass(); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Subject.IsCSharpStatic()) .FailWith("Expected type {0} not to be static{reason}.", Subject); @@ -880,26 +896,27 @@ public AndWhichConstraint HaveProperty( Guard.ThrowIfArgumentIsNull(propertyType); Guard.ThrowIfArgumentIsNullOrEmpty(name); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( - $"Expected {propertyType.Name} {{context:type}}.{name} to exist{{reason}}, but {{context:type}} is ."); + $"Cannot determine if a type has a property named {name} if the type is ."); PropertyInfo propertyInfo = null; - if (success) + if (assertionChain.Succeeded) { propertyInfo = Subject.FindPropertyByName(name); - var propertyInfoDescription = PropertyInfoAssertions.GetDescriptionFor(propertyInfo); - Execute.Assertion + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier ? assertionChain.CallerIdentifier : Subject!.Name; + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(propertyInfo is not null) - .FailWith($"Expected {propertyType.Name} {Subject}.{name} to exist{{reason}}, but it does not.") + .FailWith($"Expected {subjectDescription} to have a property {name} of type {propertyType.Name}{{reason}}, but it does not.") .Then .ForCondition(propertyInfo.PropertyType == propertyType) - .FailWith($"Expected {propertyInfoDescription} to be of type {propertyType}{{reason}}, but it is not."); + .FailWith($"Expected property {propertyInfo.Name} to be of type {propertyType}{{reason}}, but it is not.", propertyInfo); } return new AndWhichConstraint(this, propertyInfo); @@ -944,20 +961,21 @@ public AndConstraint NotHaveProperty(string name, { Guard.ThrowIfArgumentIsNullOrEmpty(name); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) - .FailWith($"Expected {{context:type}}.{name} to not exist{{reason}}, but {{context:type}} is ."); + .FailWith($"Cannot determine if a type has an unexpected property named {name} if the type is ."); - if (success) + if (assertionChain.Succeeded) { + var subjectDescription = assertionChain.HasOverriddenCallerIdentifier ? assertionChain.CallerIdentifier : Subject!.Name; + PropertyInfo propertyInfo = Subject.FindPropertyByName(name); - var propertyInfoDescription = PropertyInfoAssertions.GetDescriptionFor(propertyInfo); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(propertyInfo is null) - .FailWith($"Expected {propertyInfoDescription} to not exist{{reason}}, but it does."); + .FailWith($"Did not expect {subjectDescription} to have a property {propertyInfo?.Name}{{reason}}, but it does."); } return new AndConstraint(this); @@ -986,27 +1004,22 @@ public AndConstraint HaveExplicitProperty( Guard.ThrowIfArgumentIsNull(interfaceType); Guard.ThrowIfArgumentIsNullOrEmpty(name); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( $"Expected {{context:type}} to explicitly implement {interfaceType}.{name}{{reason}}" + ", but {context:type} is ."); - if (success) + if (assertionChain.Succeeded && AssertSubjectImplements(interfaceType, because, becauseArgs)) { - success = AssertSubjectImplements(interfaceType, because, becauseArgs); - - if (success) - { - var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name); - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(explicitlyImplementsProperty) - .FailWith( - $"Expected {Subject} to explicitly implement {interfaceType}.{name}{{reason}}, but it does not."); - } + var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name); + + assertionChain + .BecauseOf(because, becauseArgs) + .ForCondition(explicitlyImplementsProperty) + .FailWith( + $"Expected {Subject} to explicitly implement {interfaceType}.{name}{{reason}}, but it does not."); } return new AndConstraint(this); @@ -1058,28 +1071,23 @@ public AndConstraint NotHaveExplicitProperty( Guard.ThrowIfArgumentIsNull(interfaceType); Guard.ThrowIfArgumentIsNullOrEmpty(name); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( $"Expected {{context:type}} to not explicitly implement {interfaceType}.{name}{{reason}}" + ", but {context:type} is ."); - if (success) + if (assertionChain.Succeeded && AssertSubjectImplements(interfaceType, because, becauseArgs)) { - success = AssertSubjectImplements(interfaceType, because, becauseArgs); - - if (success) - { - var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name); - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(!explicitlyImplementsProperty) - .FailWith( - $"Expected {Subject} to not explicitly implement {interfaceType}.{name}{{reason}}" + - ", but it does."); - } + var explicitlyImplementsProperty = Subject.HasExplicitlyImplementedProperty(interfaceType, name); + + assertionChain + .BecauseOf(because, becauseArgs) + .ForCondition(!explicitlyImplementsProperty) + .FailWith( + $"Expected {Subject} to not explicitly implement {interfaceType}.{name}{{reason}}" + + ", but it does."); } return new AndConstraint(this); @@ -1134,28 +1142,23 @@ public AndConstraint HaveExplicitMethod( Guard.ThrowIfArgumentIsNullOrEmpty(name); Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( $"Expected {{context:type}} to explicitly implement {interfaceType}.{name}" + $"({GetParameterString(parameterTypes)}){{reason}}, but {{context:type}} is ."); - if (success) + if (assertionChain.Succeeded && AssertSubjectImplements(interfaceType, because, becauseArgs)) { - success = AssertSubjectImplements(interfaceType, because, becauseArgs); - - if (success) - { - var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes); - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(explicitlyImplementsMethod) - .FailWith( - $"Expected {Subject} to explicitly implement {interfaceType}.{name}" + - $"({GetParameterString(parameterTypes)}){{reason}}, but it does not."); - } + var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes); + + assertionChain + .BecauseOf(because, becauseArgs) + .ForCondition(explicitlyImplementsMethod) + .FailWith( + $"Expected {Subject} to explicitly implement {interfaceType}.{name}" + + $"({GetParameterString(parameterTypes)}){{reason}}, but it does not."); } return new AndConstraint(this); @@ -1212,28 +1215,23 @@ public AndConstraint NotHaveExplicitMethod( Guard.ThrowIfArgumentIsNullOrEmpty(name); Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( $"Expected {{context:type}} to not explicitly implement {interfaceType}.{name}" + $"({GetParameterString(parameterTypes)}){{reason}}, but {{context:type}} is ."); - if (success) + if (assertionChain.Succeeded && AssertSubjectImplements(interfaceType, because, becauseArgs)) { - success = AssertSubjectImplements(interfaceType, because, becauseArgs); - - if (success) - { - var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes); - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(!explicitlyImplementsMethod) - .FailWith( - $"Expected {Subject} to not explicitly implement {interfaceType}.{name}" + - $"({GetParameterString(parameterTypes)}){{reason}}, but it does."); - } + var explicitlyImplementsMethod = Subject.HasMethod($"{interfaceType}.{name}", parameterTypes); + + assertionChain + .BecauseOf(because, becauseArgs) + .ForCondition(!explicitlyImplementsMethod) + .FailWith( + $"Expected {Subject} to not explicitly implement {interfaceType}.{name}" + + $"({GetParameterString(parameterTypes)}){{reason}}, but it does."); } return new AndConstraint(this); @@ -1286,32 +1284,34 @@ public AndWhichConstraint HaveIndexer( Guard.ThrowIfArgumentIsNull(indexerType); Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + string parameterString = GetParameterString(parameterTypes); + + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( - $"Expected {indexerType.Name} {{context:type}}[{GetParameterString(parameterTypes)}] to exist{{reason}}" + + $"Expected {indexerType.Name} {{context:type}}[{parameterString}] to exist{{reason}}" + ", but {context:type} is ."); PropertyInfo propertyInfo = null; - if (success) + if (assertionChain.Succeeded) { propertyInfo = Subject.GetIndexerByParameterTypes(parameterTypes); - var propertyInfoDescription = PropertyInfoAssertions.GetDescriptionFor(propertyInfo); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(propertyInfo is not null) .FailWith( - $"Expected {indexerType.Name} {Subject}[{GetParameterString(parameterTypes)}] to exist{{reason}}" + + $"Expected {indexerType.Name} {Subject}[{parameterString}] to exist{{reason}}" + ", but it does not.") .Then .ForCondition(propertyInfo.PropertyType == indexerType) - .FailWith($"Expected {propertyInfoDescription} to be of type {indexerType}{{reason}}, but it is not."); + .FailWith("Expected {0} to be of type {1}{reason}, but it is not.", propertyInfo, indexerType); } - return new AndWhichConstraint(this, propertyInfo); + return new AndWhichConstraint(this, propertyInfo, assertionChain, + $"[{parameterString}]"); } /// @@ -1333,18 +1333,18 @@ public AndConstraint NotHaveIndexer( { Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( $"Expected indexer {{context:type}}[{GetParameterString(parameterTypes)}] to not exist{{reason}}" + ", but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { PropertyInfo propertyInfo = Subject.GetIndexerByParameterTypes(parameterTypes); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(propertyInfo is null) .FailWith( @@ -1378,7 +1378,7 @@ public AndWhichConstraint HaveMethod( Guard.ThrowIfArgumentIsNullOrEmpty(name); Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( @@ -1387,11 +1387,11 @@ public AndWhichConstraint HaveMethod( MethodInfo methodInfo = null; - if (success) + if (assertionChain.Succeeded) { methodInfo = Subject.GetMethod(name, parameterTypes); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(methodInfo is not null) .FailWith( @@ -1425,19 +1425,19 @@ public AndConstraint NotHaveMethod( Guard.ThrowIfArgumentIsNullOrEmpty(name); Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( $"Expected method {{context:type}}.{name}({GetParameterString(parameterTypes)}) to not exist{{reason}}" + ", but {context:type} is ."); - if (success) + if (assertionChain.Succeeded) { MethodInfo methodInfo = Subject.GetMethod(name, parameterTypes); var methodInfoDescription = MethodInfoAssertions.GetDescriptionFor(methodInfo); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(methodInfo is null) .FailWith( @@ -1466,7 +1466,7 @@ public AndWhichConstraint HaveConstructor( { Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( @@ -1475,11 +1475,11 @@ public AndWhichConstraint HaveConstructor( ConstructorInfo constructorInfo = null; - if (success) + if (assertionChain.Succeeded) { constructorInfo = Subject.GetConstructor(parameterTypes); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(constructorInfo is not null) .FailWith( @@ -1524,7 +1524,7 @@ public AndWhichConstraint NotHaveConstructor( { Guard.ThrowIfArgumentIsNull(parameterTypes); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( @@ -1533,11 +1533,11 @@ public AndWhichConstraint NotHaveConstructor( ConstructorInfo constructorInfo = null; - if (success) + if (assertionChain.Succeeded) { constructorInfo = Subject.GetConstructor(parameterTypes); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(constructorInfo is null) .FailWith( @@ -1588,16 +1588,16 @@ public AndConstraint HaveAccessModifier( { Guard.ThrowIfArgumentIsOutOfRange(accessModifier); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith($"Expected {{context:type}} to be {accessModifier}{{reason}}, but {{context:type}} is ."); - if (success) + if (assertionChain.Succeeded) { CSharpAccessModifier subjectAccessModifier = Subject.GetCSharpAccessModifier(); - Execute.Assertion.ForCondition(accessModifier == subjectAccessModifier) + assertionChain.ForCondition(accessModifier == subjectAccessModifier) .BecauseOf(because, becauseArgs) .ForCondition(accessModifier == subjectAccessModifier) .FailWith( @@ -1627,16 +1627,16 @@ public AndConstraint NotHaveAccessModifier( { Guard.ThrowIfArgumentIsOutOfRange(accessModifier); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith($"Expected {{context:type}} not to be {accessModifier}{{reason}}, but {{context:type}} is ."); - if (success) + if (assertionChain.Succeeded) { CSharpAccessModifier subjectAccessModifier = Subject.GetCSharpAccessModifier(); - Execute.Assertion + assertionChain .ForCondition(accessModifier != subjectAccessModifier) .BecauseOf(because, becauseArgs) .ForCondition(accessModifier != subjectAccessModifier) @@ -1687,7 +1687,7 @@ public AndWhichConstraint HaveImplicitConversionOper Guard.ThrowIfArgumentIsNull(sourceType); Guard.ThrowIfArgumentIsNull(targetType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected public static implicit {0}({1}) to exist{reason}, but {context:type} is .", @@ -1695,18 +1695,18 @@ public AndWhichConstraint HaveImplicitConversionOper MethodInfo methodInfo = null; - if (success) + if (assertionChain.Succeeded) { methodInfo = Subject.GetImplicitConversionOperator(sourceType, targetType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(methodInfo is not null) .FailWith("Expected public static implicit {0}({1}) to exist{reason}, but it does not.", targetType, sourceType); } - return new AndWhichConstraint(this, methodInfo); + return new AndWhichConstraint(this, methodInfo, assertionChain); } /// @@ -1750,17 +1750,17 @@ public AndConstraint NotHaveImplicitConversionOperator( Guard.ThrowIfArgumentIsNull(sourceType); Guard.ThrowIfArgumentIsNull(targetType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected public static implicit {0}({1}) to not exist{reason}, but {context:type} is .", targetType, sourceType); - if (success) + if (assertionChain.Succeeded) { MethodInfo methodInfo = Subject.GetImplicitConversionOperator(sourceType, targetType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(methodInfo is null) .FailWith("Expected public static implicit {0}({1}) to not exist{reason}, but it does.", @@ -1811,7 +1811,7 @@ public AndWhichConstraint HaveExplicitConversionOper Guard.ThrowIfArgumentIsNull(sourceType); Guard.ThrowIfArgumentIsNull(targetType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected public static explicit {0}({1}) to exist{reason}, but {context:type} is .", @@ -1819,11 +1819,11 @@ public AndWhichConstraint HaveExplicitConversionOper MethodInfo methodInfo = null; - if (success) + if (assertionChain.Succeeded) { methodInfo = Subject.GetExplicitConversionOperator(sourceType, targetType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(methodInfo is not null) .FailWith("Expected public static explicit {0}({1}) to exist{reason}, but it does not.", @@ -1874,17 +1874,17 @@ public AndConstraint NotHaveExplicitConversionOperator( Guard.ThrowIfArgumentIsNull(sourceType); Guard.ThrowIfArgumentIsNull(targetType); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected public static explicit {0}({1}) to not exist{reason}, but {context:type} is .", targetType, sourceType); - if (success) + if (assertionChain.Succeeded) { MethodInfo methodInfo = Subject.GetExplicitConversionOperator(sourceType, targetType); - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(methodInfo is null) .FailWith("Expected public static explicit {0}({1}) to not exist{reason}, but it does.", diff --git a/Src/FluentAssertions/Types/TypeSelectorAssertions.cs b/Src/FluentAssertions/Types/TypeSelectorAssertions.cs index 1fa57f2532..41711ae15f 100644 --- a/Src/FluentAssertions/Types/TypeSelectorAssertions.cs +++ b/Src/FluentAssertions/Types/TypeSelectorAssertions.cs @@ -18,12 +18,15 @@ namespace FluentAssertions.Types; [DebuggerNonUserCode] public class TypeSelectorAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// /// is or contains . - public TypeSelectorAssertions(params Type[] types) + public TypeSelectorAssertions(AssertionChain assertionChain, params Type[] types) { + this.assertionChain = assertionChain; Guard.ThrowIfArgumentIsNull(types); Guard.ThrowIfArgumentContainsNull(types); @@ -52,7 +55,7 @@ public AndConstraint BeDecoratedWith([String .Where(type => !type.IsDecoratedWith()) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithoutAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to be decorated with {0}{reason}," + @@ -89,7 +92,7 @@ public AndConstraint BeDecoratedWith( .Where(type => !type.IsDecoratedWith(isMatchingAttributePredicate)) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithoutMatchingAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to be decorated with {0} that matches {1}{reason}," + @@ -119,7 +122,7 @@ public AndConstraint BeDecoratedWithOrInherit !type.IsDecoratedWithOrInherit()) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithoutAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to be decorated with or inherit {0}{reason}," + @@ -156,7 +159,7 @@ public AndConstraint BeDecoratedWithOrInherit !type.IsDecoratedWithOrInherit(isMatchingAttributePredicate)) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithoutMatchingAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to be decorated with or inherit {0} that matches {1}{reason}," + @@ -185,7 +188,7 @@ public AndConstraint NotBeDecoratedWith([Str .Where(type => type.IsDecoratedWith()) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to not be decorated with {0}{reason}," + @@ -222,7 +225,7 @@ public AndConstraint NotBeDecoratedWith( .Where(type => type.IsDecoratedWith(isMatchingAttributePredicate)) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithMatchingAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to not be decorated with {0} that matches {1}{reason}," + @@ -252,7 +255,7 @@ public AndConstraint NotBeDecoratedWithOrInherit type.IsDecoratedWithOrInherit()) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to not be decorated with or inherit {0}{reason}," + @@ -289,7 +292,7 @@ public AndConstraint NotBeDecoratedWithOrInherit type.IsDecoratedWithOrInherit(isMatchingAttributePredicate)) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesWithMatchingAttribute.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to not be decorated with or inherit {0} that matches {1}{reason}," + @@ -315,7 +318,7 @@ public AndConstraint BeSealed([StringSyntax("CompositeFo { var notSealedTypes = Subject.Where(type => !type.IsCSharpSealed()).ToArray(); - Execute.Assertion.ForCondition(notSealedTypes.Length == 0) + assertionChain.ForCondition(notSealedTypes.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to be sealed{reason}, but the following types are not:" + Environment.NewLine + "{0}.", GetDescriptionsFor(notSealedTypes)); @@ -337,7 +340,7 @@ public AndConstraint NotBeSealed([StringSyntax("Composit { var sealedTypes = Subject.Where(type => type.IsCSharpSealed()).ToArray(); - Execute.Assertion.ForCondition(sealedTypes.Length == 0) + assertionChain.ForCondition(sealedTypes.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types not to be sealed{reason}, but the following types are:" + Environment.NewLine + "{0}.", GetDescriptionsFor(sealedTypes)); @@ -365,7 +368,7 @@ public AndConstraint BeInNamespace(string @namespace, .Where(t => t.Namespace != @namespace) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesNotInNamespace.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected all types to be in namespace {0}{reason}," + @@ -396,7 +399,7 @@ public AndConstraint NotBeInNamespace(string @namespace, .Where(t => t.Namespace == @namespace) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesInNamespace.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected no types to be in namespace {0}{reason}," + @@ -427,7 +430,7 @@ public AndConstraint BeUnderNamespace(string @namespace, .Where(t => !t.IsUnderNamespace(@namespace)) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesNotUnderNamespace.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected the namespaces of all types to start with {0}{reason}," + @@ -459,7 +462,7 @@ public AndConstraint NotBeUnderNamespace(string @namespa .Where(t => t.IsUnderNamespace(@namespace)) .ToArray(); - Execute.Assertion + assertionChain .ForCondition(typesUnderNamespace.Length == 0) .BecauseOf(because, becauseArgs) .FailWith("Expected the namespaces of all types to not start with {0}{reason}," + diff --git a/Src/FluentAssertions/Xml/Equivalency/XmlReaderValidator.cs b/Src/FluentAssertions/Xml/Equivalency/XmlReaderValidator.cs index 52a223417e..b82b4bba6f 100644 --- a/Src/FluentAssertions/Xml/Equivalency/XmlReaderValidator.cs +++ b/Src/FluentAssertions/Xml/Equivalency/XmlReaderValidator.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Xml; using FluentAssertions.Execution; @@ -9,16 +8,17 @@ namespace FluentAssertions.Xml.Equivalency; internal class XmlReaderValidator { - private readonly AssertionScope assertion; + private readonly AssertionChain assertionChain; private readonly XmlReader subjectReader; private readonly XmlReader expectationReader; private XmlIterator subjectIterator; private XmlIterator expectationIterator; private Node currentNode = Node.CreateRoot(); - public XmlReaderValidator(XmlReader subjectReader, XmlReader expectationReader, [StringSyntax("CompositeFormat")] string because, object[] becauseArgs) + public XmlReaderValidator(AssertionChain assertionChain, XmlReader subjectReader, XmlReader expectationReader, string because, object[] becauseArgs) { - assertion = Execute.Assertion.BecauseOf(because, becauseArgs); + this.assertionChain = assertionChain; + assertionChain.BecauseOf(because, becauseArgs); this.subjectReader = subjectReader; this.expectationReader = expectationReader; @@ -30,12 +30,12 @@ public void Validate(bool shouldBeEquivalent) if (shouldBeEquivalent && failure is not null) { - assertion.FailWith(failure.FormatString, failure.FormatParams); + assertionChain.FailWith(failure.FormatString, failure.FormatParams); } if (!shouldBeEquivalent && failure is null) { - assertion.FailWith("Did not expect {context:subject} to be equivalent{reason}, but it is."); + assertionChain.FailWith("Did not expect {context:subject} to be equivalent{reason}, but it is."); } } diff --git a/Src/FluentAssertions/Xml/XAttributeAssertions.cs b/Src/FluentAssertions/Xml/XAttributeAssertions.cs index 807ee8ee75..5c63862d5c 100644 --- a/Src/FluentAssertions/Xml/XAttributeAssertions.cs +++ b/Src/FluentAssertions/Xml/XAttributeAssertions.cs @@ -12,12 +12,15 @@ namespace FluentAssertions.Xml; [DebuggerNonUserCode] public class XAttributeAssertions : ReferenceTypeAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public XAttributeAssertions(XAttribute attribute) - : base(attribute) + public XAttributeAssertions(XAttribute attribute, AssertionChain assertionChain) + : base(attribute, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -34,7 +37,7 @@ public XAttributeAssertions(XAttribute attribute) public AndConstraint Be(XAttribute expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject?.Name == expected?.Name && Subject?.Value == expected?.Value) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to be {0}{reason}, but found {1}.", expected, Subject); @@ -57,7 +60,7 @@ public AndConstraint Be(XAttribute expected, public AndConstraint NotBe(XAttribute unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(!(Subject?.Name == unexpected?.Name && Subject?.Value == unexpected?.Value)) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context} to be {0}{reason}.", unexpected); @@ -79,14 +82,14 @@ public AndConstraint NotBe(XAttribute unexpected, public AndConstraint HaveValue(string expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the attribute to have value {0}{reason}, but {context:member} is .", expected); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject!.Value == expected) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} \"{0}\" to have value {1}{reason}, but found {2}.", diff --git a/Src/FluentAssertions/Xml/XDocumentAssertions.cs b/Src/FluentAssertions/Xml/XDocumentAssertions.cs index 98b2662d7a..f4c39fb40d 100644 --- a/Src/FluentAssertions/Xml/XDocumentAssertions.cs +++ b/Src/FluentAssertions/Xml/XDocumentAssertions.cs @@ -18,12 +18,15 @@ namespace FluentAssertions.Xml; [DebuggerNonUserCode] public class XDocumentAssertions : ReferenceTypeAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public XDocumentAssertions(XDocument document) - : base(document) + public XDocumentAssertions(XDocument document, AssertionChain assertionChain) + : base(document, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -41,7 +44,7 @@ public XDocumentAssertions(XDocument document) public AndConstraint Be(XDocument expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Equals(Subject, expected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:subject} to be {0}{reason}, but found {1}.", expected, Subject); @@ -64,7 +67,7 @@ public AndConstraint Be(XDocument expected, public AndConstraint NotBe(XDocument unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(!Equals(Subject, unexpected)) .FailWith("Did not expect {context:subject} to be {0}{reason}.", unexpected); @@ -90,7 +93,7 @@ public AndConstraint BeEquivalentTo(XDocument expected, using (XmlReader subjectReader = Subject?.CreateReader()) using (XmlReader otherReader = expected?.CreateReader()) { - var xmlReaderValidator = new XmlReaderValidator(subjectReader, otherReader, because, becauseArgs); + var xmlReaderValidator = new XmlReaderValidator(assertionChain, subjectReader, otherReader, because, becauseArgs); xmlReaderValidator.Validate(shouldBeEquivalent: true); } @@ -115,7 +118,7 @@ public AndConstraint NotBeEquivalentTo(XDocument unexpected using (XmlReader subjectReader = Subject?.CreateReader()) using (XmlReader otherReader = unexpected?.CreateReader()) { - var xmlReaderValidator = new XmlReaderValidator(subjectReader, otherReader, because, becauseArgs); + var xmlReaderValidator = new XmlReaderValidator(assertionChain, subjectReader, otherReader, because, becauseArgs); xmlReaderValidator.Validate(shouldBeEquivalent: false); } @@ -171,14 +174,14 @@ public AndWhichConstraint HaveRoot(XName expected XElement root = Subject.Root; - Execute.Assertion + assertionChain .ForCondition(root is not null && root.Name == expected) .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:subject} to have root element {0}{reason}, but found {1}.", expected.ToString(), Subject); - return new AndWhichConstraint(this, root); + return new AndWhichConstraint(this, root, assertionChain, $"/{expected}"); } /// @@ -259,7 +262,7 @@ public AndWhichConstraint HaveElement(XName expec Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot assert the document has an element if the expected name is ."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject.Root is not null) .BecauseOf(because, becauseArgs) .FailWith( @@ -268,11 +271,11 @@ public AndWhichConstraint HaveElement(XName expec XElement xElement = null; - if (success) + if (assertionChain.Succeeded) { xElement = Subject.Root!.Element(expected); - Execute.Assertion + assertionChain .ForCondition(xElement is not null) .BecauseOf(because, becauseArgs) .FailWith( @@ -280,7 +283,7 @@ public AndWhichConstraint HaveElement(XName expec expected.ToString()); } - return new AndWhichConstraint(this, xElement); + return new AndWhichConstraint(this, xElement, assertionChain, "/" + expected); } /// @@ -308,30 +311,30 @@ public AndWhichConstraint> HaveElemen Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot assert the document has an element count if the element name is ."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Cannot assert the count if the document itself is ."); IEnumerable xElements = []; - if (success) + if (assertionChain.Succeeded) { var root = Subject!.Root; - success = Execute.Assertion + assertionChain .ForCondition(root is not null) .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:subject} to have root element containing a child {0}{reason}, but it has no root element.", expected.ToString()); - if (success) + if (assertionChain.Succeeded) { xElements = root!.Elements(expected); int actual = xElements.Count(); - Execute.Assertion + assertionChain .ForConstraint(occurrenceConstraint, actual) .BecauseOf(because, becauseArgs) .FailWith( @@ -341,7 +344,7 @@ public AndWhichConstraint> HaveElemen } } - return new AndWhichConstraint>(this, xElements); + return new AndWhichConstraint>(this, xElements, assertionChain, "/" + expected); } /// diff --git a/Src/FluentAssertions/Xml/XElementAssertions.cs b/Src/FluentAssertions/Xml/XElementAssertions.cs index 3381c4703b..c01c43748d 100644 --- a/Src/FluentAssertions/Xml/XElementAssertions.cs +++ b/Src/FluentAssertions/Xml/XElementAssertions.cs @@ -18,12 +18,15 @@ namespace FluentAssertions.Xml; [DebuggerNonUserCode] public class XElementAssertions : ReferenceTypeAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// - public XElementAssertions(XElement xElement) - : base(xElement) + public XElementAssertions(XElement xElement, AssertionChain assertionChain) + : base(xElement, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -42,7 +45,7 @@ public XElementAssertions(XElement xElement) public AndConstraint Be(XElement expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(XNode.DeepEquals(Subject, expected)) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:subject} to be {0}{reason}, but found {1}.", expected, Subject); @@ -66,7 +69,7 @@ public AndConstraint Be(XElement expected, public AndConstraint NotBe(XElement unexpected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition((Subject is null && unexpected is not null) || !XNode.DeepEquals(Subject, unexpected)) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context:subject} to be {0}{reason}.", unexpected); @@ -93,7 +96,7 @@ public AndConstraint BeEquivalentTo(XElement expected, using (XmlReader subjectReader = Subject?.CreateReader()) using (XmlReader expectedReader = expected?.CreateReader()) { - var xmlReaderValidator = new XmlReaderValidator(subjectReader, expectedReader, because, becauseArgs); + var xmlReaderValidator = new XmlReaderValidator(assertionChain, subjectReader, expectedReader, because, becauseArgs); xmlReaderValidator.Validate(shouldBeEquivalent: true); } @@ -119,7 +122,7 @@ public AndConstraint NotBeEquivalentTo(XElement unexpected, using (XmlReader subjectReader = Subject?.CreateReader()) using (XmlReader otherReader = unexpected?.CreateReader()) { - var xmlReaderValidator = new XmlReaderValidator(subjectReader, otherReader, because, becauseArgs); + var xmlReaderValidator = new XmlReaderValidator(assertionChain, subjectReader, otherReader, because, becauseArgs); xmlReaderValidator.Validate(shouldBeEquivalent: false); } @@ -140,14 +143,14 @@ public AndConstraint NotBeEquivalentTo(XElement unexpected, public AndConstraint HaveValue(string expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith("Expected the element to have value {0}{reason}, but {context:member} is .", expected); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(Subject!.Value == expected) .BecauseOf(because, becauseArgs) .FailWith( @@ -202,18 +205,18 @@ public AndConstraint HaveAttribute(XName expectedName, strin string expectedText = expectedName.ToString(); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( "Expected attribute {0} in element to have value {1}{reason}, but {context:member} is .", expectedText, expectedValue); - if (success) + if (assertionChain.Succeeded) { XAttribute attribute = Subject!.Attribute(expectedName); - success = Execute.Assertion + assertionChain .ForCondition(attribute is not null) .BecauseOf(because, becauseArgs) .FailWith( @@ -221,9 +224,9 @@ public AndConstraint HaveAttribute(XName expectedName, strin + " but found no such attribute in {2}", expectedText, expectedValue, Subject); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(attribute!.Value == expectedValue) .BecauseOf(because, becauseArgs) .FailWith( @@ -275,7 +278,7 @@ public AndWhichConstraint HaveElement(XName expect { Guard.ThrowIfArgumentIsNull(expected); - bool success = Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .ForCondition(Subject is not null) .FailWith( @@ -284,11 +287,11 @@ public AndWhichConstraint HaveElement(XName expect XElement xElement = null; - if (success) + if (assertionChain.Succeeded) { xElement = Subject!.Element(expected); - Execute.Assertion + assertionChain .ForCondition(xElement is not null) .BecauseOf(because, becauseArgs) .FailWith( @@ -296,7 +299,7 @@ public AndWhichConstraint HaveElement(XName expect expected.ToString().EscapePlaceholders()); } - return new AndWhichConstraint(this, xElement); + return new AndWhichConstraint(this, xElement, assertionChain, "/" + expected); } /// @@ -324,7 +327,7 @@ public AndWhichConstraint> HaveElement Guard.ThrowIfArgumentIsNull(expected, nameof(expected), "Cannot assert the element has an element count if the element name is ."); - bool success = Execute.Assertion + assertionChain .ForCondition(Subject is not null) .BecauseOf(because, becauseArgs) .FailWith( @@ -333,12 +336,12 @@ public AndWhichConstraint> HaveElement IEnumerable xElements = []; - if (success) + if (assertionChain.Succeeded) { xElements = Subject!.Elements(expected); int actual = xElements.Count(); - Execute.Assertion + assertionChain .ForConstraint(occurrenceConstraint, actual) .BecauseOf(because, becauseArgs) .FailWith( @@ -347,7 +350,7 @@ public AndWhichConstraint> HaveElement expected.ToString()); } - return new AndWhichConstraint>(this, xElements); + return new AndWhichConstraint>(this, xElements, assertionChain, "/" + expected); } /// diff --git a/Src/FluentAssertions/Xml/XmlElementAssertions.cs b/Src/FluentAssertions/Xml/XmlElementAssertions.cs index 60bfd53dff..651eda0fd0 100644 --- a/Src/FluentAssertions/Xml/XmlElementAssertions.cs +++ b/Src/FluentAssertions/Xml/XmlElementAssertions.cs @@ -13,13 +13,16 @@ namespace FluentAssertions.Xml; [DebuggerNonUserCode] public class XmlElementAssertions : XmlNodeAssertions { + private readonly AssertionChain assertionChain; + /// /// Initializes a new instance of the class. /// /// - public XmlElementAssertions(XmlElement xmlElement) - : base(xmlElement) + public XmlElementAssertions(XmlElement xmlElement, AssertionChain assertionChain) + : base(xmlElement, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -37,7 +40,7 @@ public XmlElementAssertions(XmlElement xmlElement) public AndConstraint HaveInnerText(string expected, [StringSyntax("CompositeFormat")] string because = "", params object[] becauseArgs) { - Execute.Assertion + assertionChain .ForCondition(Subject.InnerText == expected) .BecauseOf(because, becauseArgs) .FailWith( @@ -94,7 +97,7 @@ public AndConstraint HaveAttributeWithNamespace( (string.IsNullOrEmpty(expectedNamespace) ? string.Empty : $"{{{expectedNamespace}}}") + expectedName; - bool success = Execute.Assertion + assertionChain .ForCondition(attribute is not null) .BecauseOf(because, becauseArgs) .FailWith( @@ -102,9 +105,9 @@ public AndConstraint HaveAttributeWithNamespace( + " with value {1}{reason}, but found no such attribute in {2}", expectedFormattedName, expectedValue, Subject); - if (success) + if (assertionChain.Succeeded) { - Execute.Assertion + assertionChain .ForCondition(attribute!.Value == expectedValue) .BecauseOf(because, becauseArgs) .FailWith( @@ -158,14 +161,14 @@ public AndWhichConstraint HaveElementWithNames (string.IsNullOrEmpty(expectedNamespace) ? string.Empty : $"{{{expectedNamespace}}}") + expectedName; - Execute.Assertion + assertionChain .ForCondition(element is not null) .BecauseOf(because, becauseArgs) .FailWith( "Expected {context:subject} to have child element {0}{reason}, but no such child element was found.", expectedFormattedName.EscapePlaceholders()); - return new AndWhichConstraint(this, element); + return new AndWhichConstraint(this, element, assertionChain, "/" + expectedName); } protected override string Identifier => "XML element"; diff --git a/Src/FluentAssertions/Xml/XmlNodeAssertions.cs b/Src/FluentAssertions/Xml/XmlNodeAssertions.cs index 779317b295..1a3356ee65 100644 --- a/Src/FluentAssertions/Xml/XmlNodeAssertions.cs +++ b/Src/FluentAssertions/Xml/XmlNodeAssertions.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Xml; +using FluentAssertions.Execution; using FluentAssertions.Primitives; using FluentAssertions.Xml.Equivalency; @@ -12,8 +13,8 @@ namespace FluentAssertions.Xml; [DebuggerNonUserCode] public class XmlNodeAssertions : XmlNodeAssertions { - public XmlNodeAssertions(XmlNode xmlNode) - : base(xmlNode) + public XmlNodeAssertions(XmlNode xmlNode, AssertionChain assertionChain) + : base(xmlNode, assertionChain) { } } @@ -26,9 +27,12 @@ public class XmlNodeAssertions : ReferenceTypeAssertions< where TSubject : XmlNode where TAssertions : XmlNodeAssertions { - public XmlNodeAssertions(TSubject xmlNode) - : base(xmlNode) + private readonly AssertionChain assertionChain; + + public XmlNodeAssertions(TSubject xmlNode, AssertionChain assertionChain) + : base(xmlNode, assertionChain) { + this.assertionChain = assertionChain; } /// @@ -48,7 +52,7 @@ public AndConstraint BeEquivalentTo(XmlNode expected, using (var subjectReader = new XmlNodeReader(Subject)) using (var expectedReader = new XmlNodeReader(expected)) { - var xmlReaderValidator = new XmlReaderValidator(subjectReader, expectedReader, because, becauseArgs); + var xmlReaderValidator = new XmlReaderValidator(assertionChain, subjectReader, expectedReader, because, becauseArgs); xmlReaderValidator.Validate(shouldBeEquivalent: true); } @@ -73,7 +77,7 @@ public AndConstraint NotBeEquivalentTo(XmlNode unexpected, using (var subjectReader = new XmlNodeReader(Subject)) using (var unexpectedReader = new XmlNodeReader(unexpected)) { - var xmlReaderValidator = new XmlReaderValidator(subjectReader, unexpectedReader, because, becauseArgs); + var xmlReaderValidator = new XmlReaderValidator(assertionChain, subjectReader, unexpectedReader, because, becauseArgs); xmlReaderValidator.Validate(shouldBeEquivalent: false); } diff --git a/Src/FluentAssertions/XmlAssertionExtensions.cs b/Src/FluentAssertions/XmlAssertionExtensions.cs index 2db9562915..79da764778 100644 --- a/Src/FluentAssertions/XmlAssertionExtensions.cs +++ b/Src/FluentAssertions/XmlAssertionExtensions.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Xml; +using FluentAssertions.Execution; using FluentAssertions.Xml; namespace FluentAssertions; @@ -10,11 +11,11 @@ public static class XmlAssertionExtensions { public static XmlNodeAssertions Should([NotNull] this XmlNode actualValue) { - return new XmlNodeAssertions(actualValue); + return new XmlNodeAssertions(actualValue, AssertionChain.GetOrCreate()); } public static XmlElementAssertions Should([NotNull] this XmlElement actualValue) { - return new XmlElementAssertions(actualValue); + return new XmlElementAssertions(actualValue, AssertionChain.GetOrCreate()); } } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt index ce88279d5e..d80352ffdd 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt @@ -15,12 +15,14 @@ namespace FluentAssertions public AndConstraint(TParent parent) { } public TParent And { get; } } - public class AndWhichConstraint : FluentAssertions.AndConstraint + public class AndWhichConstraint : FluentAssertions.AndConstraint { - public AndWhichConstraint(TParentConstraint parentConstraint, System.Collections.Generic.IEnumerable matchedConstraint) { } - public AndWhichConstraint(TParentConstraint parentConstraint, TMatchedElement matchedConstraint) { } - public TMatchedElement Subject { get; } - public TMatchedElement Which { get; } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects) { } + public AndWhichConstraint(TParent parent, TSubject subject) { } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix) { } + public AndWhichConstraint(TParent parent, TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix = "") { } + public TSubject Subject { get; } + public TSubject Which { get; } } public static class AssertionExtensions { @@ -67,7 +69,6 @@ namespace FluentAssertions public static FluentAssertions.Specialized.NonGenericAsyncFunctionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Func action) { } public static FluentAssertions.Primitives.GuidAssertions Should(this System.Guid actualValue) { } public static FluentAssertions.Primitives.NullableGuidAssertions Should(this System.Guid? actualValue) { } - public static FluentAssertions.Streams.BufferedStreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.BufferedStream actualValue) { } public static FluentAssertions.Streams.StreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.Stream actualValue) { } public static FluentAssertions.Primitives.HttpResponseMessageAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Net.Http.HttpResponseMessage actualValue) { } public static FluentAssertions.Types.AssemblyAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Reflection.Assembly assembly) { } @@ -376,18 +377,18 @@ namespace FluentAssertions.Collections { public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions, T, FluentAssertions.Collections.GenericCollectionAssertions> { - public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.GenericCollectionAssertions { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint AllBeAssignableTo(System.Type expectedType, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint> AllBeAssignableTo(string because = "", params object[] becauseArgs) { } @@ -461,7 +462,7 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint NotBeSubsetOf(System.Collections.Generic.IEnumerable unexpectedSuperset, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Collections.Generic.IEnumerable unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndWhichConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainInConsecutiveOrder(params T[] unexpected) { } @@ -492,13 +493,13 @@ namespace FluentAssertions.Collections public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericDictionaryAssertions> where TCollection : System.Collections.Generic.IEnumerable> { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericCollectionAssertions, TAssertions> where TCollection : System.Collections.Generic.IEnumerable> where TAssertions : FluentAssertions.Collections.GenericDictionaryAssertions { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } @@ -510,8 +511,8 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint ContainKeys(params TKey[] expected) { } public FluentAssertions.AndConstraint ContainKeys(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint ContainValue(TValue expected, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndConstraint ContainValues(params TValue[] expected) { } - public FluentAssertions.AndConstraint ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint> ContainValues(params TValue[] expected) { } + public FluentAssertions.AndWhichConstraint> ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Equal(T expected, string because = "", params object[] becauseArgs) where T : System.Collections.Generic.IEnumerable> { } public FluentAssertions.AndConstraint NotContain(params System.Collections.Generic.KeyValuePair[] items) { } @@ -529,18 +530,18 @@ namespace FluentAssertions.Collections } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> { - public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.StringCollectionAssertions { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint AllBe(string expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint AllBe(string expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(params string[] expectation) { } @@ -553,18 +554,18 @@ namespace FluentAssertions.Collections } public class SubsequentOrderingAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions, T, FluentAssertions.Collections.SubsequentOrderingAssertions> { - public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, System.Collections.Generic.IComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInDescendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } @@ -705,7 +706,7 @@ namespace FluentAssertions.Equivalency { protected EquivalencyStep() { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } - protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested); + protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator); } public class EquivalencyValidationContext : FluentAssertions.Equivalency.IEquivalencyValidationContext { @@ -797,7 +798,7 @@ namespace FluentAssertions.Equivalency } public interface IMemberMatchingRule { - FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options); + FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options, FluentAssertions.Execution.AssertionChain assertionChain); } public interface IMemberSelectionRule { @@ -959,7 +960,7 @@ namespace FluentAssertions.Equivalency.Steps { public class AssertionRuleEquivalencyStep : FluentAssertions.Equivalency.IEquivalencyStep { - public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertion) { } + public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertionAction) { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } public override string ToString() { } } @@ -972,7 +973,7 @@ namespace FluentAssertions.Equivalency.Steps public class DictionaryEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public DictionaryEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class EnumEqualityStep : FluentAssertions.Equivalency.IEquivalencyStep { @@ -1033,17 +1034,17 @@ namespace FluentAssertions.Equivalency.Steps public class XAttributeEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XAttributeEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XDocumentEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XDocumentEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XElementEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XElementEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } } namespace FluentAssertions.Equivalency.Tracing @@ -1073,7 +1074,7 @@ namespace FluentAssertions.Events { public class EventAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> { - protected EventAssertions(FluentAssertions.Events.IMonitor monitor) { } + protected EventAssertions(FluentAssertions.Events.IMonitor monitor, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.Events.IMonitor Monitor { get; } public void NotRaise(string eventName, string because = "", params object[] becauseArgs) { } @@ -1119,76 +1120,65 @@ namespace FluentAssertions.Events } namespace FluentAssertions.Execution { + public sealed class AssertionChain + { + public string CallerIdentifier { get; } + public bool HasOverriddenCallerIdentifier { get; } + public bool PreviousAssertionSucceeded { get; } + public bool Succeeded { get; } + public FluentAssertions.Execution.AssertionChain UsingLineBreaks { get; } + public void AddPreFormattedFailure(string failure) { } + public void AddReportable(string key, System.Func getValue) { } + public void AddReportable(string key, string value) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(FluentAssertions.Execution.Reason reason) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(string because, params object[] becauseArgs) { } + public FluentAssertions.Execution.Continuation FailWith(System.Func getFailureReason) { } + public FluentAssertions.Execution.Continuation FailWith(string message) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } + public FluentAssertions.Execution.AssertionChain ForCondition(bool condition) { } + public FluentAssertions.Execution.AssertionChain ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } + public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } + public void OverrideCallerIdentifier(System.Func getCallerIdentifier) { } + public void ReuseOnce() { } + public FluentAssertions.Execution.AssertionChain WithCallerPostfix(string postfix) { } + public FluentAssertions.Execution.AssertionChain WithCallerPrefix(string prefix) { } + public FluentAssertions.Execution.AssertionChain WithDefaultIdentifier(string identifier) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, object arg2, System.Action chain) { } + public FluentAssertions.Execution.AssertionChain WithReportable(string name, System.Func content) { } + public static FluentAssertions.Execution.AssertionChain GetOrCreate() { } + } [System.Serializable] public class AssertionFailedException : System.Exception { public AssertionFailedException(string message) { } protected AssertionFailedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } - public sealed class AssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable + public sealed class AssertionScope : System.IDisposable { public AssertionScope() { } public AssertionScope(FluentAssertions.Execution.IAssertionStrategy assertionStrategy) { } - public AssertionScope(System.Lazy context) { } - public AssertionScope(string context) { } - public string CallerIdentity { get; } - public System.Lazy Context { get; set; } + public AssertionScope(System.Func name) { } + public AssertionScope(string name) { } public FluentAssertions.Formatting.FormattingOptions FormattingOptions { get; } - public FluentAssertions.Execution.AssertionScope UsingLineBreaks { get; } + public System.Func Name { get; } public static FluentAssertions.Execution.AssertionScope Current { get; } - public void AddNonReportable(string key, object value) { } public void AddPreFormattedFailure(string formattedFailureMessage) { } - public void AddReportable(string key, System.Func valueFunc) { } - public void AddReportable(string key, string value) { } public void AppendTracing(string tracingBlock) { } - public void AssumeSingleCaller() { } - public FluentAssertions.Execution.AssertionScope BecauseOf(FluentAssertions.Execution.Reason reason) { } - public FluentAssertions.Execution.AssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } public string[] Discard() { } public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.AssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.AssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public T Get(string key) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } public bool HasFailures() { } - public FluentAssertions.Execution.AssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.AssertionScope WithExpectation(string message, params object[] args) { } } public class Continuation { - public FluentAssertions.Execution.IAssertionScope Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.Continuation continuation) { } + public FluentAssertions.Execution.AssertionChain Then { get; } } public class ContinuationOfGiven { + public bool Succeeded { get; } public FluentAssertions.Execution.GivenSelector Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.ContinuationOfGiven continuationOfGiven) { } - } - public sealed class ContinuedAssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable - { - public FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - public FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } - public string[] Discard() { } - public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.IAssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } - public FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args) { } - } - public static class Execute - { - public static FluentAssertions.Execution.AssertionScope Assertion { get; } } public class FailReason { @@ -1198,29 +1188,13 @@ namespace FluentAssertions.Execution } public class GivenSelector { - public FluentAssertions.Execution.ContinuationOfGiven ClearExpectation() { } + public bool Succeeded { get; } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params System.Func[] args) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params object[] args) { } public FluentAssertions.Execution.GivenSelector ForCondition(System.Func predicate) { } public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } } - public interface IAssertionScope : System.IDisposable - { - FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs); - FluentAssertions.Execution.Continuation ClearExpectation(); - string[] Discard(); - FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc); - FluentAssertions.Execution.Continuation FailWith(string message); - FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders); - FluentAssertions.Execution.Continuation FailWith(string message, params object[] args); - FluentAssertions.Execution.IAssertionScope ForCondition(bool condition); - FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences); - FluentAssertions.Execution.GivenSelector Given(System.Func selector); - FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier); - FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args); - } public interface IAssertionStrategy { System.Collections.Generic.IEnumerable FailureMessages { get; } @@ -1463,6 +1437,12 @@ namespace FluentAssertions.Formatting public MaxLinesExceededException(string message) { } public MaxLinesExceededException(string message, System.Exception innerException) { } } + public class MethodInfoFormatter : FluentAssertions.Formatting.IValueFormatter + { + public MethodInfoFormatter() { } + public bool CanHandle(object value) { } + public void Format(object value, FluentAssertions.Formatting.FormattedObjectGraph formattedGraph, FluentAssertions.Formatting.FormattingContext context, FluentAssertions.Formatting.FormatChild formatChild) { } + } public class MultidimensionalArrayFormatter : FluentAssertions.Formatting.IValueFormatter { public MultidimensionalArrayFormatter() { } @@ -1569,12 +1549,12 @@ namespace FluentAssertions.Numeric { public class ComparableTypeAssertions : FluentAssertions.Numeric.ComparableTypeAssertions> { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class ComparableTypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, TAssertions> where TAssertions : FluentAssertions.Numeric.ComparableTypeAssertions { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } @@ -1594,13 +1574,13 @@ namespace FluentAssertions.Numeric public class NullableNumericAssertions : FluentAssertions.Numeric.NullableNumericAssertions> where T : struct, System.IComparable { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableNumericAssertions : FluentAssertions.Numeric.NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NullableNumericAssertions { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Match(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } @@ -1610,13 +1590,14 @@ namespace FluentAssertions.Numeric public class NumericAssertions : FluentAssertions.Numeric.NumericAssertions> where T : struct, System.IComparable { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NumericAssertions { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } public T? Subject { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(T? expected, string because = "", params object[] becauseArgs) { } @@ -1642,12 +1623,12 @@ namespace FluentAssertions.Primitives { public class BooleanAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class BooleanAssertions where TAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public bool? Subject { get; } public FluentAssertions.AndConstraint Be(bool expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeFalse(string because = "", params object[] becauseArgs) { } @@ -1658,12 +1639,12 @@ namespace FluentAssertions.Primitives } public class DateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTime? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTime expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTime? expected, string because = "", params object[] becauseArgs) { } @@ -1708,12 +1689,12 @@ namespace FluentAssertions.Primitives } public class DateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTimeOffset? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTimeOffset expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTimeOffset? expected, string because = "", params object[] becauseArgs) { } @@ -1763,7 +1744,7 @@ namespace FluentAssertions.Primitives public class DateTimeOffsetRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1771,7 +1752,7 @@ namespace FluentAssertions.Primitives public class DateTimeRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - protected DateTimeRangeAssertions(TAssertions parentAssertions, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTime target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTime target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1779,13 +1760,13 @@ namespace FluentAssertions.Primitives public class EnumAssertions : FluentAssertions.Primitives.EnumAssertions> where TEnum : struct, System.Enum { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.EnumAssertions { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public TEnum? Subject { get; } public FluentAssertions.AndConstraint Be(TEnum expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TEnum? expected, string because = "", params object[] becauseArgs) { } @@ -1812,12 +1793,12 @@ namespace FluentAssertions.Primitives } public class GuidAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GuidAssertions where TAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.Guid? Subject { get; } public FluentAssertions.AndConstraint Be(System.Guid expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } @@ -1829,12 +1810,12 @@ namespace FluentAssertions.Primitives } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { } @@ -1846,12 +1827,12 @@ namespace FluentAssertions.Primitives } public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableBooleanAssertions : FluentAssertions.Primitives.BooleanAssertions where TAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(bool? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1863,12 +1844,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1876,12 +1857,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1890,13 +1871,13 @@ namespace FluentAssertions.Primitives public class NullableEnumAssertions : FluentAssertions.Primitives.NullableEnumAssertions> where TEnum : struct, System.Enum { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableEnumAssertions : FluentAssertions.Primitives.EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.NullableEnumAssertions { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1904,12 +1885,12 @@ namespace FluentAssertions.Primitives } public class NullableGuidAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableGuidAssertions : FluentAssertions.Primitives.GuidAssertions where TAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.Guid? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1918,12 +1899,12 @@ namespace FluentAssertions.Primitives } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.TimeSpan? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1932,7 +1913,7 @@ namespace FluentAssertions.Primitives } public class ObjectAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(object value) { } + public ObjectAssertions(object value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(TExpectation expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeOneOf(System.Collections.Generic.IEnumerable validValues, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBe(TExpectation unexpected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -1940,7 +1921,7 @@ namespace FluentAssertions.Primitives public class ObjectAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(TSubject value) { } + public ObjectAssertions(TSubject value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(TSubject expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TSubject expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -1958,7 +1939,8 @@ namespace FluentAssertions.Primitives public abstract class ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - protected ReferenceTypeAssertions(TSubject subject) { } + protected ReferenceTypeAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } protected abstract string Identifier { get; } public TSubject Subject { get; } public FluentAssertions.AndConstraint BeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { } @@ -1982,12 +1964,12 @@ namespace FluentAssertions.Primitives } public class SimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.TimeSpan? Subject { get; } public FluentAssertions.AndConstraint Be(System.TimeSpan expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan nearbyTime, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } @@ -2003,12 +1985,12 @@ namespace FluentAssertions.Primitives } public class StringAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEmpty(string because = "", params object[] becauseArgs) { } @@ -2084,8 +2066,8 @@ namespace FluentAssertions.Specialized { public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } @@ -2095,7 +2077,7 @@ namespace FluentAssertions.Specialized where TTask : System.Threading.Tasks.Task where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } public System.Threading.Tasks.Task> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) @@ -2121,7 +2103,7 @@ namespace FluentAssertions.Specialized where TDelegate : System.Delegate where TAssertions : FluentAssertions.Specialized.DelegateAssertions { - protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } + protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } protected abstract void InvokeSubject(); public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } @@ -2133,7 +2115,7 @@ namespace FluentAssertions.Specialized public class ExceptionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, FluentAssertions.Specialized.ExceptionAssertions> where TException : System.Exception { - public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions) { } + public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions, FluentAssertions.Execution.AssertionChain assertionChain) { } public TException And { get; } protected override string Identifier { get; } public TException Which { get; } @@ -2155,7 +2137,7 @@ namespace FluentAssertions.Specialized } public class ExecutionTimeAssertions { - public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime) { } + public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan expectedDuration, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThan(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThanOrEqualTo(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } @@ -2165,8 +2147,8 @@ namespace FluentAssertions.Specialized } public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } @@ -2174,8 +2156,8 @@ namespace FluentAssertions.Specialized } public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } @@ -2191,8 +2173,8 @@ namespace FluentAssertions.Specialized } public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } @@ -2204,33 +2186,23 @@ namespace FluentAssertions.Specialized } public class TaskCompletionSourceAssertions : FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase { - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs) { } - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Common.IClock clock) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task>> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } } namespace FluentAssertions.Streams { - public class BufferedStreamAssertions : FluentAssertions.Streams.BufferedStreamAssertions - { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } - } - public class BufferedStreamAssertions : FluentAssertions.Streams.StreamAssertions - where TAssertions : FluentAssertions.Streams.BufferedStreamAssertions - { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } - protected override string Identifier { get; } - } public class StreamAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(System.IO.Stream stream) { } + public StreamAssertions(System.IO.Stream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StreamAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.IO.Stream where TAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(TSubject stream) { } + public StreamAssertions(TSubject stream, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeReadOnly(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } @@ -2256,7 +2228,7 @@ namespace FluentAssertions.Types } public class AssemblyAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public AssemblyAssertions(System.Reflection.Assembly assembly) { } + public AssemblyAssertions(System.Reflection.Assembly assembly, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeSignedWithPublicKey(string publicKey, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeUnsigned(string because = "", params object[] becauseArgs) { } @@ -2266,15 +2238,17 @@ namespace FluentAssertions.Types } public class ConstructorInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo) { } + public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } } public abstract class MemberInfoAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Reflection.MemberInfo where TAssertions : FluentAssertions.Types.MemberInfoAssertions { - protected MemberInfoAssertions(TSubject subject) { } + protected MemberInfoAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected virtual string SubjectDescription { get; } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(System.Linq.Expressions.Expression> isMatchingAttributePredicate, string because = "", params object[] becauseArgs) @@ -2288,14 +2262,15 @@ namespace FluentAssertions.Types where TSubject : System.Reflection.MethodBase where TAssertions : FluentAssertions.Types.MethodBaseAssertions { - protected MethodBaseAssertions(TSubject subject) { } + protected MethodBaseAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint HaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotHaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } } public class MethodInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo) { } + public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeAsync(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeAsync(string because = "", params object[] becauseArgs) { } @@ -2338,7 +2313,7 @@ namespace FluentAssertions.Types } public class MethodInfoSelectorAssertions { - public MethodInfoSelectorAssertions(params System.Reflection.MethodInfo[] methods) { } + public MethodInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.MethodInfo[] methods) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectMethods { get; } public FluentAssertions.AndConstraint Be(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } @@ -2359,8 +2334,9 @@ namespace FluentAssertions.Types } public class PropertyInfoAssertions : FluentAssertions.Types.MemberInfoAssertions { - public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo) { } + public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } @@ -2401,7 +2377,7 @@ namespace FluentAssertions.Types } public class PropertyInfoSelectorAssertions { - public PropertyInfoSelectorAssertions(params System.Reflection.PropertyInfo[] properties) { } + public PropertyInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.PropertyInfo[] properties) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectProperties { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) @@ -2416,7 +2392,7 @@ namespace FluentAssertions.Types } public class TypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public TypeAssertions(System.Type type) { } + public TypeAssertions(System.Type type, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Type expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string because = "", params object[] becauseArgs) { } @@ -2534,7 +2510,7 @@ namespace FluentAssertions.Types } public class TypeSelectorAssertions { - public TypeSelectorAssertions(params System.Type[] types) { } + public TypeSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Type[] types) { } public System.Collections.Generic.IEnumerable Subject { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } @@ -2565,7 +2541,7 @@ namespace FluentAssertions.Xml { public class XAttributeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XAttributeAssertions(System.Xml.Linq.XAttribute attribute) { } + public XAttributeAssertions(System.Xml.Linq.XAttribute attribute, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XAttribute expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string expected, string because = "", params object[] becauseArgs) { } @@ -2573,7 +2549,7 @@ namespace FluentAssertions.Xml } public class XDocumentAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XDocumentAssertions(System.Xml.Linq.XDocument document) { } + public XDocumentAssertions(System.Xml.Linq.XDocument document, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } @@ -2588,7 +2564,7 @@ namespace FluentAssertions.Xml } public class XElementAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XElementAssertions(System.Xml.Linq.XElement xElement) { } + public XElementAssertions(System.Xml.Linq.XElement xElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } @@ -2604,7 +2580,7 @@ namespace FluentAssertions.Xml } public class XmlElementAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlElementAssertions(System.Xml.XmlElement xmlElement) { } + public XmlElementAssertions(System.Xml.XmlElement xmlElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint HaveAttribute(string expectedName, string expectedValue, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveAttributeWithNamespace(string expectedName, string expectedNamespace, string expectedValue, string because = "", params object[] becauseArgs) { } @@ -2614,13 +2590,13 @@ namespace FluentAssertions.Xml } public class XmlNodeAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(System.Xml.XmlNode xmlNode) { } + public XmlNodeAssertions(System.Xml.XmlNode xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class XmlNodeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Xml.XmlNode where TAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(TSubject xmlNode) { } + public XmlNodeAssertions(TSubject xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.XmlNode expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeEquivalentTo(System.Xml.XmlNode unexpected, string because = "", params object[] becauseArgs) { } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt index c9ad572ad9..156569c2ba 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt @@ -15,12 +15,14 @@ namespace FluentAssertions public AndConstraint(TParent parent) { } public TParent And { get; } } - public class AndWhichConstraint : FluentAssertions.AndConstraint + public class AndWhichConstraint : FluentAssertions.AndConstraint { - public AndWhichConstraint(TParentConstraint parentConstraint, System.Collections.Generic.IEnumerable matchedConstraint) { } - public AndWhichConstraint(TParentConstraint parentConstraint, TMatchedElement matchedConstraint) { } - public TMatchedElement Subject { get; } - public TMatchedElement Which { get; } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects) { } + public AndWhichConstraint(TParent parent, TSubject subject) { } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix) { } + public AndWhichConstraint(TParent parent, TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix = "") { } + public TSubject Subject { get; } + public TSubject Which { get; } } public static class AssertionExtensions { @@ -389,18 +391,18 @@ namespace FluentAssertions.Collections { public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions, T, FluentAssertions.Collections.GenericCollectionAssertions> { - public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.GenericCollectionAssertions { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint AllBeAssignableTo(System.Type expectedType, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint> AllBeAssignableTo(string because = "", params object[] becauseArgs) { } @@ -474,7 +476,7 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint NotBeSubsetOf(System.Collections.Generic.IEnumerable unexpectedSuperset, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Collections.Generic.IEnumerable unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndWhichConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainInConsecutiveOrder(params T[] unexpected) { } @@ -505,13 +507,13 @@ namespace FluentAssertions.Collections public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericDictionaryAssertions> where TCollection : System.Collections.Generic.IEnumerable> { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericCollectionAssertions, TAssertions> where TCollection : System.Collections.Generic.IEnumerable> where TAssertions : FluentAssertions.Collections.GenericDictionaryAssertions { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } @@ -523,8 +525,8 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint ContainKeys(params TKey[] expected) { } public FluentAssertions.AndConstraint ContainKeys(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint ContainValue(TValue expected, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndConstraint ContainValues(params TValue[] expected) { } - public FluentAssertions.AndConstraint ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint> ContainValues(params TValue[] expected) { } + public FluentAssertions.AndWhichConstraint> ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Equal(T expected, string because = "", params object[] becauseArgs) where T : System.Collections.Generic.IEnumerable> { } public FluentAssertions.AndConstraint NotContain(params System.Collections.Generic.KeyValuePair[] items) { } @@ -542,18 +544,18 @@ namespace FluentAssertions.Collections } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> { - public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.StringCollectionAssertions { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint AllBe(string expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint AllBe(string expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(params string[] expectation) { } @@ -566,18 +568,18 @@ namespace FluentAssertions.Collections } public class SubsequentOrderingAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions, T, FluentAssertions.Collections.SubsequentOrderingAssertions> { - public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, System.Collections.Generic.IComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInDescendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } @@ -718,7 +720,7 @@ namespace FluentAssertions.Equivalency { protected EquivalencyStep() { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } - protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested); + protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator); } public class EquivalencyValidationContext : FluentAssertions.Equivalency.IEquivalencyValidationContext { @@ -810,7 +812,7 @@ namespace FluentAssertions.Equivalency } public interface IMemberMatchingRule { - FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options); + FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options, FluentAssertions.Execution.AssertionChain assertionChain); } public interface IMemberSelectionRule { @@ -972,7 +974,7 @@ namespace FluentAssertions.Equivalency.Steps { public class AssertionRuleEquivalencyStep : FluentAssertions.Equivalency.IEquivalencyStep { - public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertion) { } + public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertionAction) { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } public override string ToString() { } } @@ -985,7 +987,7 @@ namespace FluentAssertions.Equivalency.Steps public class DictionaryEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public DictionaryEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class EnumEqualityStep : FluentAssertions.Equivalency.IEquivalencyStep { @@ -1046,17 +1048,17 @@ namespace FluentAssertions.Equivalency.Steps public class XAttributeEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XAttributeEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XDocumentEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XDocumentEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XElementEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XElementEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } } namespace FluentAssertions.Equivalency.Tracing @@ -1086,7 +1088,7 @@ namespace FluentAssertions.Events { public class EventAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> { - protected EventAssertions(FluentAssertions.Events.IMonitor monitor) { } + protected EventAssertions(FluentAssertions.Events.IMonitor monitor, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.Events.IMonitor Monitor { get; } public void NotRaise(string eventName, string because = "", params object[] becauseArgs) { } @@ -1132,76 +1134,65 @@ namespace FluentAssertions.Events } namespace FluentAssertions.Execution { + public sealed class AssertionChain + { + public string CallerIdentifier { get; } + public bool HasOverriddenCallerIdentifier { get; } + public bool PreviousAssertionSucceeded { get; } + public bool Succeeded { get; } + public FluentAssertions.Execution.AssertionChain UsingLineBreaks { get; } + public void AddPreFormattedFailure(string failure) { } + public void AddReportable(string key, System.Func getValue) { } + public void AddReportable(string key, string value) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(FluentAssertions.Execution.Reason reason) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(string because, params object[] becauseArgs) { } + public FluentAssertions.Execution.Continuation FailWith(System.Func getFailureReason) { } + public FluentAssertions.Execution.Continuation FailWith(string message) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } + public FluentAssertions.Execution.AssertionChain ForCondition(bool condition) { } + public FluentAssertions.Execution.AssertionChain ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } + public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } + public void OverrideCallerIdentifier(System.Func getCallerIdentifier) { } + public void ReuseOnce() { } + public FluentAssertions.Execution.AssertionChain WithCallerPostfix(string postfix) { } + public FluentAssertions.Execution.AssertionChain WithCallerPrefix(string prefix) { } + public FluentAssertions.Execution.AssertionChain WithDefaultIdentifier(string identifier) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, object arg2, System.Action chain) { } + public FluentAssertions.Execution.AssertionChain WithReportable(string name, System.Func content) { } + public static FluentAssertions.Execution.AssertionChain GetOrCreate() { } + } [System.Serializable] public class AssertionFailedException : System.Exception { public AssertionFailedException(string message) { } protected AssertionFailedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } - public sealed class AssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable + public sealed class AssertionScope : System.IDisposable { public AssertionScope() { } public AssertionScope(FluentAssertions.Execution.IAssertionStrategy assertionStrategy) { } - public AssertionScope(System.Lazy context) { } - public AssertionScope(string context) { } - public string CallerIdentity { get; } - public System.Lazy Context { get; set; } + public AssertionScope(System.Func name) { } + public AssertionScope(string name) { } public FluentAssertions.Formatting.FormattingOptions FormattingOptions { get; } - public FluentAssertions.Execution.AssertionScope UsingLineBreaks { get; } + public System.Func Name { get; } public static FluentAssertions.Execution.AssertionScope Current { get; } - public void AddNonReportable(string key, object value) { } public void AddPreFormattedFailure(string formattedFailureMessage) { } - public void AddReportable(string key, System.Func valueFunc) { } - public void AddReportable(string key, string value) { } public void AppendTracing(string tracingBlock) { } - public void AssumeSingleCaller() { } - public FluentAssertions.Execution.AssertionScope BecauseOf(FluentAssertions.Execution.Reason reason) { } - public FluentAssertions.Execution.AssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } public string[] Discard() { } public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.AssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.AssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public T Get(string key) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } public bool HasFailures() { } - public FluentAssertions.Execution.AssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.AssertionScope WithExpectation(string message, params object[] args) { } } public class Continuation { - public FluentAssertions.Execution.IAssertionScope Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.Continuation continuation) { } + public FluentAssertions.Execution.AssertionChain Then { get; } } public class ContinuationOfGiven { + public bool Succeeded { get; } public FluentAssertions.Execution.GivenSelector Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.ContinuationOfGiven continuationOfGiven) { } - } - public sealed class ContinuedAssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable - { - public FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - public FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } - public string[] Discard() { } - public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.IAssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } - public FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args) { } - } - public static class Execute - { - public static FluentAssertions.Execution.AssertionScope Assertion { get; } } public class FailReason { @@ -1211,29 +1202,13 @@ namespace FluentAssertions.Execution } public class GivenSelector { - public FluentAssertions.Execution.ContinuationOfGiven ClearExpectation() { } + public bool Succeeded { get; } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params System.Func[] args) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params object[] args) { } public FluentAssertions.Execution.GivenSelector ForCondition(System.Func predicate) { } public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } } - public interface IAssertionScope : System.IDisposable - { - FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs); - FluentAssertions.Execution.Continuation ClearExpectation(); - string[] Discard(); - FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc); - FluentAssertions.Execution.Continuation FailWith(string message); - FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders); - FluentAssertions.Execution.Continuation FailWith(string message, params object[] args); - FluentAssertions.Execution.IAssertionScope ForCondition(bool condition); - FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences); - FluentAssertions.Execution.GivenSelector Given(System.Func selector); - FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier); - FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args); - } public interface IAssertionStrategy { System.Collections.Generic.IEnumerable FailureMessages { get; } @@ -1482,6 +1457,12 @@ namespace FluentAssertions.Formatting public MaxLinesExceededException(string message) { } public MaxLinesExceededException(string message, System.Exception innerException) { } } + public class MethodInfoFormatter : FluentAssertions.Formatting.IValueFormatter + { + public MethodInfoFormatter() { } + public bool CanHandle(object value) { } + public void Format(object value, FluentAssertions.Formatting.FormattedObjectGraph formattedGraph, FluentAssertions.Formatting.FormattingContext context, FluentAssertions.Formatting.FormatChild formatChild) { } + } public class MultidimensionalArrayFormatter : FluentAssertions.Formatting.IValueFormatter { public MultidimensionalArrayFormatter() { } @@ -1594,12 +1575,12 @@ namespace FluentAssertions.Numeric { public class ComparableTypeAssertions : FluentAssertions.Numeric.ComparableTypeAssertions> { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class ComparableTypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, TAssertions> where TAssertions : FluentAssertions.Numeric.ComparableTypeAssertions { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } @@ -1619,13 +1600,13 @@ namespace FluentAssertions.Numeric public class NullableNumericAssertions : FluentAssertions.Numeric.NullableNumericAssertions> where T : struct, System.IComparable { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableNumericAssertions : FluentAssertions.Numeric.NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NullableNumericAssertions { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Match(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } @@ -1635,13 +1616,14 @@ namespace FluentAssertions.Numeric public class NumericAssertions : FluentAssertions.Numeric.NumericAssertions> where T : struct, System.IComparable { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NumericAssertions { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } public T? Subject { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(T? expected, string because = "", params object[] becauseArgs) { } @@ -1667,12 +1649,12 @@ namespace FluentAssertions.Primitives { public class BooleanAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class BooleanAssertions where TAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public bool? Subject { get; } public FluentAssertions.AndConstraint Be(bool expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeFalse(string because = "", params object[] becauseArgs) { } @@ -1683,12 +1665,12 @@ namespace FluentAssertions.Primitives } public class DateOnlyAssertions : FluentAssertions.Primitives.DateOnlyAssertions { - public DateOnlyAssertions(System.DateOnly? value) { } + public DateOnlyAssertions(System.DateOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateOnlyAssertions where TAssertions : FluentAssertions.Primitives.DateOnlyAssertions { - public DateOnlyAssertions(System.DateOnly? value) { } + public DateOnlyAssertions(System.DateOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateOnly? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateOnly expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateOnly? expected, string because = "", params object[] becauseArgs) { } @@ -1716,12 +1698,12 @@ namespace FluentAssertions.Primitives } public class DateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTime? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTime expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTime? expected, string because = "", params object[] becauseArgs) { } @@ -1766,12 +1748,12 @@ namespace FluentAssertions.Primitives } public class DateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTimeOffset? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTimeOffset expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTimeOffset? expected, string because = "", params object[] becauseArgs) { } @@ -1821,7 +1803,7 @@ namespace FluentAssertions.Primitives public class DateTimeOffsetRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1829,7 +1811,7 @@ namespace FluentAssertions.Primitives public class DateTimeRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - protected DateTimeRangeAssertions(TAssertions parentAssertions, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTime target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTime target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1837,13 +1819,13 @@ namespace FluentAssertions.Primitives public class EnumAssertions : FluentAssertions.Primitives.EnumAssertions> where TEnum : struct, System.Enum { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.EnumAssertions { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public TEnum? Subject { get; } public FluentAssertions.AndConstraint Be(TEnum expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TEnum? expected, string because = "", params object[] becauseArgs) { } @@ -1870,12 +1852,12 @@ namespace FluentAssertions.Primitives } public class GuidAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GuidAssertions where TAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.Guid? Subject { get; } public FluentAssertions.AndConstraint Be(System.Guid expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } @@ -1887,12 +1869,12 @@ namespace FluentAssertions.Primitives } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { } @@ -1904,12 +1886,12 @@ namespace FluentAssertions.Primitives } public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableBooleanAssertions : FluentAssertions.Primitives.BooleanAssertions where TAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(bool? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1921,12 +1903,12 @@ namespace FluentAssertions.Primitives } public class NullableDateOnlyAssertions : FluentAssertions.Primitives.NullableDateOnlyAssertions { - public NullableDateOnlyAssertions(System.DateOnly? value) { } + public NullableDateOnlyAssertions(System.DateOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateOnlyAssertions : FluentAssertions.Primitives.DateOnlyAssertions where TAssertions : FluentAssertions.Primitives.NullableDateOnlyAssertions { - public NullableDateOnlyAssertions(System.DateOnly? value) { } + public NullableDateOnlyAssertions(System.DateOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1934,12 +1916,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1947,12 +1929,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1961,13 +1943,13 @@ namespace FluentAssertions.Primitives public class NullableEnumAssertions : FluentAssertions.Primitives.NullableEnumAssertions> where TEnum : struct, System.Enum { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableEnumAssertions : FluentAssertions.Primitives.EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.NullableEnumAssertions { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1975,12 +1957,12 @@ namespace FluentAssertions.Primitives } public class NullableGuidAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableGuidAssertions : FluentAssertions.Primitives.GuidAssertions where TAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.Guid? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1989,12 +1971,12 @@ namespace FluentAssertions.Primitives } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.TimeSpan? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -2003,12 +1985,12 @@ namespace FluentAssertions.Primitives } public class NullableTimeOnlyAssertions : FluentAssertions.Primitives.NullableTimeOnlyAssertions { - public NullableTimeOnlyAssertions(System.TimeOnly? value) { } + public NullableTimeOnlyAssertions(System.TimeOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableTimeOnlyAssertions : FluentAssertions.Primitives.TimeOnlyAssertions where TAssertions : FluentAssertions.Primitives.NullableTimeOnlyAssertions { - public NullableTimeOnlyAssertions(System.TimeOnly? value) { } + public NullableTimeOnlyAssertions(System.TimeOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -2016,7 +1998,7 @@ namespace FluentAssertions.Primitives } public class ObjectAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(object value) { } + public ObjectAssertions(object value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(TExpectation expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeOneOf(System.Collections.Generic.IEnumerable validValues, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBe(TExpectation unexpected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -2024,7 +2006,7 @@ namespace FluentAssertions.Primitives public class ObjectAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(TSubject value) { } + public ObjectAssertions(TSubject value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(TSubject expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TSubject expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -2042,7 +2024,8 @@ namespace FluentAssertions.Primitives public abstract class ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - protected ReferenceTypeAssertions(TSubject subject) { } + protected ReferenceTypeAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } protected abstract string Identifier { get; } public TSubject Subject { get; } public FluentAssertions.AndConstraint BeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { } @@ -2066,12 +2049,12 @@ namespace FluentAssertions.Primitives } public class SimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.TimeSpan? Subject { get; } public FluentAssertions.AndConstraint Be(System.TimeSpan expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan nearbyTime, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } @@ -2087,12 +2070,12 @@ namespace FluentAssertions.Primitives } public class StringAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEmpty(string because = "", params object[] becauseArgs) { } @@ -2157,12 +2140,12 @@ namespace FluentAssertions.Primitives } public class TimeOnlyAssertions : FluentAssertions.Primitives.TimeOnlyAssertions { - public TimeOnlyAssertions(System.TimeOnly? value) { } + public TimeOnlyAssertions(System.TimeOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class TimeOnlyAssertions where TAssertions : FluentAssertions.Primitives.TimeOnlyAssertions { - public TimeOnlyAssertions(System.TimeOnly? value) { } + public TimeOnlyAssertions(System.TimeOnly? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.TimeOnly? Subject { get; } public FluentAssertions.AndConstraint Be(System.TimeOnly expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.TimeOnly? expected, string because = "", params object[] becauseArgs) { } @@ -2205,8 +2188,8 @@ namespace FluentAssertions.Specialized { public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } @@ -2216,7 +2199,7 @@ namespace FluentAssertions.Specialized where TTask : System.Threading.Tasks.Task where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } public System.Threading.Tasks.Task> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) @@ -2242,7 +2225,7 @@ namespace FluentAssertions.Specialized where TDelegate : System.Delegate where TAssertions : FluentAssertions.Specialized.DelegateAssertions { - protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } + protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } protected abstract void InvokeSubject(); public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } @@ -2254,7 +2237,7 @@ namespace FluentAssertions.Specialized public class ExceptionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, FluentAssertions.Specialized.ExceptionAssertions> where TException : System.Exception { - public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions) { } + public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions, FluentAssertions.Execution.AssertionChain assertionChain) { } public TException And { get; } protected override string Identifier { get; } public TException Which { get; } @@ -2276,7 +2259,7 @@ namespace FluentAssertions.Specialized } public class ExecutionTimeAssertions { - public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime) { } + public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan expectedDuration, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThan(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThanOrEqualTo(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } @@ -2286,8 +2269,8 @@ namespace FluentAssertions.Specialized } public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } @@ -2295,8 +2278,8 @@ namespace FluentAssertions.Specialized } public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } @@ -2312,16 +2295,16 @@ namespace FluentAssertions.Specialized } public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } } public class TaskCompletionSourceAssertions : FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase { - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs) { } - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Common.IClock clock) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } @@ -2332,8 +2315,8 @@ namespace FluentAssertions.Specialized } public class TaskCompletionSourceAssertions : FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase { - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs) { } - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Common.IClock clock) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task>> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } @@ -2342,25 +2325,25 @@ namespace FluentAssertions.Streams { public class BufferedStreamAssertions : FluentAssertions.Streams.BufferedStreamAssertions { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } + public BufferedStreamAssertions(System.IO.BufferedStream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class BufferedStreamAssertions : FluentAssertions.Streams.StreamAssertions where TAssertions : FluentAssertions.Streams.BufferedStreamAssertions { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } + public BufferedStreamAssertions(System.IO.BufferedStream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint HaveBufferSize(int expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotHaveBufferSize(int unexpected, string because = "", params object[] becauseArgs) { } } public class StreamAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(System.IO.Stream stream) { } + public StreamAssertions(System.IO.Stream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StreamAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.IO.Stream where TAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(TSubject stream) { } + public StreamAssertions(TSubject stream, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeReadOnly(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } @@ -2386,7 +2369,7 @@ namespace FluentAssertions.Types } public class AssemblyAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public AssemblyAssertions(System.Reflection.Assembly assembly) { } + public AssemblyAssertions(System.Reflection.Assembly assembly, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeSignedWithPublicKey(string publicKey, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeUnsigned(string because = "", params object[] becauseArgs) { } @@ -2396,15 +2379,17 @@ namespace FluentAssertions.Types } public class ConstructorInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo) { } + public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } } public abstract class MemberInfoAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Reflection.MemberInfo where TAssertions : FluentAssertions.Types.MemberInfoAssertions { - protected MemberInfoAssertions(TSubject subject) { } + protected MemberInfoAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected virtual string SubjectDescription { get; } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(System.Linq.Expressions.Expression> isMatchingAttributePredicate, string because = "", params object[] becauseArgs) @@ -2418,14 +2403,15 @@ namespace FluentAssertions.Types where TSubject : System.Reflection.MethodBase where TAssertions : FluentAssertions.Types.MethodBaseAssertions { - protected MethodBaseAssertions(TSubject subject) { } + protected MethodBaseAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint HaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotHaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } } public class MethodInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo) { } + public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeAsync(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeAsync(string because = "", params object[] becauseArgs) { } @@ -2468,7 +2454,7 @@ namespace FluentAssertions.Types } public class MethodInfoSelectorAssertions { - public MethodInfoSelectorAssertions(params System.Reflection.MethodInfo[] methods) { } + public MethodInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.MethodInfo[] methods) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectMethods { get; } public FluentAssertions.AndConstraint Be(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } @@ -2489,8 +2475,9 @@ namespace FluentAssertions.Types } public class PropertyInfoAssertions : FluentAssertions.Types.MemberInfoAssertions { - public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo) { } + public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } @@ -2531,7 +2518,7 @@ namespace FluentAssertions.Types } public class PropertyInfoSelectorAssertions { - public PropertyInfoSelectorAssertions(params System.Reflection.PropertyInfo[] properties) { } + public PropertyInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.PropertyInfo[] properties) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectProperties { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) @@ -2546,7 +2533,7 @@ namespace FluentAssertions.Types } public class TypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public TypeAssertions(System.Type type) { } + public TypeAssertions(System.Type type, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Type expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string because = "", params object[] becauseArgs) { } @@ -2664,7 +2651,7 @@ namespace FluentAssertions.Types } public class TypeSelectorAssertions { - public TypeSelectorAssertions(params System.Type[] types) { } + public TypeSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Type[] types) { } public System.Collections.Generic.IEnumerable Subject { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } @@ -2695,7 +2682,7 @@ namespace FluentAssertions.Xml { public class XAttributeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XAttributeAssertions(System.Xml.Linq.XAttribute attribute) { } + public XAttributeAssertions(System.Xml.Linq.XAttribute attribute, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XAttribute expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string expected, string because = "", params object[] becauseArgs) { } @@ -2703,7 +2690,7 @@ namespace FluentAssertions.Xml } public class XDocumentAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XDocumentAssertions(System.Xml.Linq.XDocument document) { } + public XDocumentAssertions(System.Xml.Linq.XDocument document, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } @@ -2718,7 +2705,7 @@ namespace FluentAssertions.Xml } public class XElementAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XElementAssertions(System.Xml.Linq.XElement xElement) { } + public XElementAssertions(System.Xml.Linq.XElement xElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } @@ -2734,7 +2721,7 @@ namespace FluentAssertions.Xml } public class XmlElementAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlElementAssertions(System.Xml.XmlElement xmlElement) { } + public XmlElementAssertions(System.Xml.XmlElement xmlElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint HaveAttribute(string expectedName, string expectedValue, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveAttributeWithNamespace(string expectedName, string expectedNamespace, string expectedValue, string because = "", params object[] becauseArgs) { } @@ -2744,13 +2731,13 @@ namespace FluentAssertions.Xml } public class XmlNodeAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(System.Xml.XmlNode xmlNode) { } + public XmlNodeAssertions(System.Xml.XmlNode xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class XmlNodeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Xml.XmlNode where TAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(TSubject xmlNode) { } + public XmlNodeAssertions(TSubject xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.XmlNode expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeEquivalentTo(System.Xml.XmlNode unexpected, string because = "", params object[] becauseArgs) { } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt index 1de3630d79..644f35ca50 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt @@ -15,12 +15,14 @@ namespace FluentAssertions public AndConstraint(TParent parent) { } public TParent And { get; } } - public class AndWhichConstraint : FluentAssertions.AndConstraint + public class AndWhichConstraint : FluentAssertions.AndConstraint { - public AndWhichConstraint(TParentConstraint parentConstraint, System.Collections.Generic.IEnumerable matchedConstraint) { } - public AndWhichConstraint(TParentConstraint parentConstraint, TMatchedElement matchedConstraint) { } - public TMatchedElement Subject { get; } - public TMatchedElement Which { get; } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects) { } + public AndWhichConstraint(TParent parent, TSubject subject) { } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix) { } + public AndWhichConstraint(TParent parent, TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix = "") { } + public TSubject Subject { get; } + public TSubject Which { get; } } public static class AssertionExtensions { @@ -65,7 +67,6 @@ namespace FluentAssertions public static FluentAssertions.Specialized.NonGenericAsyncFunctionAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Func action) { } public static FluentAssertions.Primitives.GuidAssertions Should(this System.Guid actualValue) { } public static FluentAssertions.Primitives.NullableGuidAssertions Should(this System.Guid? actualValue) { } - public static FluentAssertions.Streams.BufferedStreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.BufferedStream actualValue) { } public static FluentAssertions.Streams.StreamAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.IO.Stream actualValue) { } public static FluentAssertions.Primitives.HttpResponseMessageAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Net.Http.HttpResponseMessage actualValue) { } public static FluentAssertions.Types.AssemblyAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this System.Reflection.Assembly assembly) { } @@ -368,18 +369,18 @@ namespace FluentAssertions.Collections { public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions, T, FluentAssertions.Collections.GenericCollectionAssertions> { - public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.GenericCollectionAssertions { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint AllBeAssignableTo(System.Type expectedType, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint> AllBeAssignableTo(string because = "", params object[] becauseArgs) { } @@ -453,7 +454,7 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint NotBeSubsetOf(System.Collections.Generic.IEnumerable unexpectedSuperset, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Collections.Generic.IEnumerable unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndWhichConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainInConsecutiveOrder(params T[] unexpected) { } @@ -484,13 +485,13 @@ namespace FluentAssertions.Collections public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericDictionaryAssertions> where TCollection : System.Collections.Generic.IEnumerable> { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericCollectionAssertions, TAssertions> where TCollection : System.Collections.Generic.IEnumerable> where TAssertions : FluentAssertions.Collections.GenericDictionaryAssertions { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } @@ -502,8 +503,8 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint ContainKeys(params TKey[] expected) { } public FluentAssertions.AndConstraint ContainKeys(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint ContainValue(TValue expected, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndConstraint ContainValues(params TValue[] expected) { } - public FluentAssertions.AndConstraint ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint> ContainValues(params TValue[] expected) { } + public FluentAssertions.AndWhichConstraint> ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Equal(T expected, string because = "", params object[] becauseArgs) where T : System.Collections.Generic.IEnumerable> { } public FluentAssertions.AndConstraint NotContain(params System.Collections.Generic.KeyValuePair[] items) { } @@ -521,18 +522,18 @@ namespace FluentAssertions.Collections } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> { - public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.StringCollectionAssertions { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint AllBe(string expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint AllBe(string expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(params string[] expectation) { } @@ -545,18 +546,18 @@ namespace FluentAssertions.Collections } public class SubsequentOrderingAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions, T, FluentAssertions.Collections.SubsequentOrderingAssertions> { - public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, System.Collections.Generic.IComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInDescendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } @@ -697,7 +698,7 @@ namespace FluentAssertions.Equivalency { protected EquivalencyStep() { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } - protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested); + protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator); } public class EquivalencyValidationContext : FluentAssertions.Equivalency.IEquivalencyValidationContext { @@ -789,7 +790,7 @@ namespace FluentAssertions.Equivalency } public interface IMemberMatchingRule { - FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options); + FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options, FluentAssertions.Execution.AssertionChain assertionChain); } public interface IMemberSelectionRule { @@ -951,7 +952,7 @@ namespace FluentAssertions.Equivalency.Steps { public class AssertionRuleEquivalencyStep : FluentAssertions.Equivalency.IEquivalencyStep { - public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertion) { } + public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertionAction) { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } public override string ToString() { } } @@ -964,7 +965,7 @@ namespace FluentAssertions.Equivalency.Steps public class DictionaryEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public DictionaryEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class EnumEqualityStep : FluentAssertions.Equivalency.IEquivalencyStep { @@ -1025,17 +1026,17 @@ namespace FluentAssertions.Equivalency.Steps public class XAttributeEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XAttributeEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XDocumentEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XDocumentEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XElementEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XElementEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } } namespace FluentAssertions.Equivalency.Tracing @@ -1063,76 +1064,65 @@ namespace FluentAssertions.Equivalency.Tracing } namespace FluentAssertions.Execution { + public sealed class AssertionChain + { + public string CallerIdentifier { get; } + public bool HasOverriddenCallerIdentifier { get; } + public bool PreviousAssertionSucceeded { get; } + public bool Succeeded { get; } + public FluentAssertions.Execution.AssertionChain UsingLineBreaks { get; } + public void AddPreFormattedFailure(string failure) { } + public void AddReportable(string key, System.Func getValue) { } + public void AddReportable(string key, string value) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(FluentAssertions.Execution.Reason reason) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(string because, params object[] becauseArgs) { } + public FluentAssertions.Execution.Continuation FailWith(System.Func getFailureReason) { } + public FluentAssertions.Execution.Continuation FailWith(string message) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } + public FluentAssertions.Execution.AssertionChain ForCondition(bool condition) { } + public FluentAssertions.Execution.AssertionChain ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } + public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } + public void OverrideCallerIdentifier(System.Func getCallerIdentifier) { } + public void ReuseOnce() { } + public FluentAssertions.Execution.AssertionChain WithCallerPostfix(string postfix) { } + public FluentAssertions.Execution.AssertionChain WithCallerPrefix(string prefix) { } + public FluentAssertions.Execution.AssertionChain WithDefaultIdentifier(string identifier) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, object arg2, System.Action chain) { } + public FluentAssertions.Execution.AssertionChain WithReportable(string name, System.Func content) { } + public static FluentAssertions.Execution.AssertionChain GetOrCreate() { } + } [System.Serializable] public class AssertionFailedException : System.Exception { public AssertionFailedException(string message) { } protected AssertionFailedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } - public sealed class AssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable + public sealed class AssertionScope : System.IDisposable { public AssertionScope() { } public AssertionScope(FluentAssertions.Execution.IAssertionStrategy assertionStrategy) { } - public AssertionScope(System.Lazy context) { } - public AssertionScope(string context) { } - public string CallerIdentity { get; } - public System.Lazy Context { get; set; } + public AssertionScope(System.Func name) { } + public AssertionScope(string name) { } public FluentAssertions.Formatting.FormattingOptions FormattingOptions { get; } - public FluentAssertions.Execution.AssertionScope UsingLineBreaks { get; } + public System.Func Name { get; } public static FluentAssertions.Execution.AssertionScope Current { get; } - public void AddNonReportable(string key, object value) { } public void AddPreFormattedFailure(string formattedFailureMessage) { } - public void AddReportable(string key, System.Func valueFunc) { } - public void AddReportable(string key, string value) { } public void AppendTracing(string tracingBlock) { } - public void AssumeSingleCaller() { } - public FluentAssertions.Execution.AssertionScope BecauseOf(FluentAssertions.Execution.Reason reason) { } - public FluentAssertions.Execution.AssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } public string[] Discard() { } public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.AssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.AssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public T Get(string key) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } public bool HasFailures() { } - public FluentAssertions.Execution.AssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.AssertionScope WithExpectation(string message, params object[] args) { } } public class Continuation { - public FluentAssertions.Execution.IAssertionScope Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.Continuation continuation) { } + public FluentAssertions.Execution.AssertionChain Then { get; } } public class ContinuationOfGiven { + public bool Succeeded { get; } public FluentAssertions.Execution.GivenSelector Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.ContinuationOfGiven continuationOfGiven) { } - } - public sealed class ContinuedAssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable - { - public FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - public FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } - public string[] Discard() { } - public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.IAssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } - public FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args) { } - } - public static class Execute - { - public static FluentAssertions.Execution.AssertionScope Assertion { get; } } public class FailReason { @@ -1142,29 +1132,13 @@ namespace FluentAssertions.Execution } public class GivenSelector { - public FluentAssertions.Execution.ContinuationOfGiven ClearExpectation() { } + public bool Succeeded { get; } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params System.Func[] args) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params object[] args) { } public FluentAssertions.Execution.GivenSelector ForCondition(System.Func predicate) { } public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } } - public interface IAssertionScope : System.IDisposable - { - FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs); - FluentAssertions.Execution.Continuation ClearExpectation(); - string[] Discard(); - FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc); - FluentAssertions.Execution.Continuation FailWith(string message); - FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders); - FluentAssertions.Execution.Continuation FailWith(string message, params object[] args); - FluentAssertions.Execution.IAssertionScope ForCondition(bool condition); - FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences); - FluentAssertions.Execution.GivenSelector Given(System.Func selector); - FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier); - FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args); - } public interface IAssertionStrategy { System.Collections.Generic.IEnumerable FailureMessages { get; } @@ -1407,6 +1381,12 @@ namespace FluentAssertions.Formatting public MaxLinesExceededException(string message) { } public MaxLinesExceededException(string message, System.Exception innerException) { } } + public class MethodInfoFormatter : FluentAssertions.Formatting.IValueFormatter + { + public MethodInfoFormatter() { } + public bool CanHandle(object value) { } + public void Format(object value, FluentAssertions.Formatting.FormattedObjectGraph formattedGraph, FluentAssertions.Formatting.FormattingContext context, FluentAssertions.Formatting.FormatChild formatChild) { } + } public class MultidimensionalArrayFormatter : FluentAssertions.Formatting.IValueFormatter { public MultidimensionalArrayFormatter() { } @@ -1513,12 +1493,12 @@ namespace FluentAssertions.Numeric { public class ComparableTypeAssertions : FluentAssertions.Numeric.ComparableTypeAssertions> { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class ComparableTypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, TAssertions> where TAssertions : FluentAssertions.Numeric.ComparableTypeAssertions { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } @@ -1538,13 +1518,13 @@ namespace FluentAssertions.Numeric public class NullableNumericAssertions : FluentAssertions.Numeric.NullableNumericAssertions> where T : struct, System.IComparable { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableNumericAssertions : FluentAssertions.Numeric.NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NullableNumericAssertions { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Match(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } @@ -1554,13 +1534,14 @@ namespace FluentAssertions.Numeric public class NumericAssertions : FluentAssertions.Numeric.NumericAssertions> where T : struct, System.IComparable { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NumericAssertions { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } public T? Subject { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(T? expected, string because = "", params object[] becauseArgs) { } @@ -1586,12 +1567,12 @@ namespace FluentAssertions.Primitives { public class BooleanAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class BooleanAssertions where TAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public bool? Subject { get; } public FluentAssertions.AndConstraint Be(bool expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeFalse(string because = "", params object[] becauseArgs) { } @@ -1602,12 +1583,12 @@ namespace FluentAssertions.Primitives } public class DateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTime? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTime expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTime? expected, string because = "", params object[] becauseArgs) { } @@ -1652,12 +1633,12 @@ namespace FluentAssertions.Primitives } public class DateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTimeOffset? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTimeOffset expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTimeOffset? expected, string because = "", params object[] becauseArgs) { } @@ -1707,7 +1688,7 @@ namespace FluentAssertions.Primitives public class DateTimeOffsetRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1715,7 +1696,7 @@ namespace FluentAssertions.Primitives public class DateTimeRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - protected DateTimeRangeAssertions(TAssertions parentAssertions, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTime target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTime target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1723,13 +1704,13 @@ namespace FluentAssertions.Primitives public class EnumAssertions : FluentAssertions.Primitives.EnumAssertions> where TEnum : struct, System.Enum { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.EnumAssertions { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public TEnum? Subject { get; } public FluentAssertions.AndConstraint Be(TEnum expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TEnum? expected, string because = "", params object[] becauseArgs) { } @@ -1756,12 +1737,12 @@ namespace FluentAssertions.Primitives } public class GuidAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GuidAssertions where TAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.Guid? Subject { get; } public FluentAssertions.AndConstraint Be(System.Guid expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } @@ -1773,12 +1754,12 @@ namespace FluentAssertions.Primitives } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { } @@ -1790,12 +1771,12 @@ namespace FluentAssertions.Primitives } public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableBooleanAssertions : FluentAssertions.Primitives.BooleanAssertions where TAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(bool? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1807,12 +1788,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1820,12 +1801,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1834,13 +1815,13 @@ namespace FluentAssertions.Primitives public class NullableEnumAssertions : FluentAssertions.Primitives.NullableEnumAssertions> where TEnum : struct, System.Enum { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableEnumAssertions : FluentAssertions.Primitives.EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.NullableEnumAssertions { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1848,12 +1829,12 @@ namespace FluentAssertions.Primitives } public class NullableGuidAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableGuidAssertions : FluentAssertions.Primitives.GuidAssertions where TAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.Guid? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1862,12 +1843,12 @@ namespace FluentAssertions.Primitives } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.TimeSpan? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1876,7 +1857,7 @@ namespace FluentAssertions.Primitives } public class ObjectAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(object value) { } + public ObjectAssertions(object value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(TExpectation expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeOneOf(System.Collections.Generic.IEnumerable validValues, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBe(TExpectation unexpected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -1884,7 +1865,7 @@ namespace FluentAssertions.Primitives public class ObjectAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(TSubject value) { } + public ObjectAssertions(TSubject value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(TSubject expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TSubject expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -1902,7 +1883,8 @@ namespace FluentAssertions.Primitives public abstract class ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - protected ReferenceTypeAssertions(TSubject subject) { } + protected ReferenceTypeAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } protected abstract string Identifier { get; } public TSubject Subject { get; } public FluentAssertions.AndConstraint BeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { } @@ -1926,12 +1908,12 @@ namespace FluentAssertions.Primitives } public class SimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.TimeSpan? Subject { get; } public FluentAssertions.AndConstraint Be(System.TimeSpan expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan nearbyTime, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } @@ -1947,12 +1929,12 @@ namespace FluentAssertions.Primitives } public class StringAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEmpty(string because = "", params object[] becauseArgs) { } @@ -2028,8 +2010,8 @@ namespace FluentAssertions.Specialized { public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } @@ -2039,7 +2021,7 @@ namespace FluentAssertions.Specialized where TTask : System.Threading.Tasks.Task where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } public System.Threading.Tasks.Task> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) @@ -2065,7 +2047,7 @@ namespace FluentAssertions.Specialized where TDelegate : System.Delegate where TAssertions : FluentAssertions.Specialized.DelegateAssertions { - protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } + protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } protected abstract void InvokeSubject(); public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } @@ -2077,7 +2059,7 @@ namespace FluentAssertions.Specialized public class ExceptionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, FluentAssertions.Specialized.ExceptionAssertions> where TException : System.Exception { - public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions) { } + public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions, FluentAssertions.Execution.AssertionChain assertionChain) { } public TException And { get; } protected override string Identifier { get; } public TException Which { get; } @@ -2099,7 +2081,7 @@ namespace FluentAssertions.Specialized } public class ExecutionTimeAssertions { - public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime) { } + public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan expectedDuration, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThan(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThanOrEqualTo(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } @@ -2109,8 +2091,8 @@ namespace FluentAssertions.Specialized } public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } @@ -2118,8 +2100,8 @@ namespace FluentAssertions.Specialized } public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } @@ -2135,8 +2117,8 @@ namespace FluentAssertions.Specialized } public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } @@ -2148,33 +2130,23 @@ namespace FluentAssertions.Specialized } public class TaskCompletionSourceAssertions : FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase { - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs) { } - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Common.IClock clock) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task>> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } } namespace FluentAssertions.Streams { - public class BufferedStreamAssertions : FluentAssertions.Streams.BufferedStreamAssertions - { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } - } - public class BufferedStreamAssertions : FluentAssertions.Streams.StreamAssertions - where TAssertions : FluentAssertions.Streams.BufferedStreamAssertions - { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } - protected override string Identifier { get; } - } public class StreamAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(System.IO.Stream stream) { } + public StreamAssertions(System.IO.Stream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StreamAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.IO.Stream where TAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(TSubject stream) { } + public StreamAssertions(TSubject stream, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeReadOnly(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } @@ -2200,7 +2172,7 @@ namespace FluentAssertions.Types } public class AssemblyAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public AssemblyAssertions(System.Reflection.Assembly assembly) { } + public AssemblyAssertions(System.Reflection.Assembly assembly, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeSignedWithPublicKey(string publicKey, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeUnsigned(string because = "", params object[] becauseArgs) { } @@ -2210,15 +2182,17 @@ namespace FluentAssertions.Types } public class ConstructorInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo) { } + public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } } public abstract class MemberInfoAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Reflection.MemberInfo where TAssertions : FluentAssertions.Types.MemberInfoAssertions { - protected MemberInfoAssertions(TSubject subject) { } + protected MemberInfoAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected virtual string SubjectDescription { get; } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(System.Linq.Expressions.Expression> isMatchingAttributePredicate, string because = "", params object[] becauseArgs) @@ -2232,14 +2206,15 @@ namespace FluentAssertions.Types where TSubject : System.Reflection.MethodBase where TAssertions : FluentAssertions.Types.MethodBaseAssertions { - protected MethodBaseAssertions(TSubject subject) { } + protected MethodBaseAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint HaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotHaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } } public class MethodInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo) { } + public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeAsync(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeAsync(string because = "", params object[] becauseArgs) { } @@ -2282,7 +2257,7 @@ namespace FluentAssertions.Types } public class MethodInfoSelectorAssertions { - public MethodInfoSelectorAssertions(params System.Reflection.MethodInfo[] methods) { } + public MethodInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.MethodInfo[] methods) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectMethods { get; } public FluentAssertions.AndConstraint Be(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } @@ -2303,8 +2278,9 @@ namespace FluentAssertions.Types } public class PropertyInfoAssertions : FluentAssertions.Types.MemberInfoAssertions { - public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo) { } + public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } @@ -2345,7 +2321,7 @@ namespace FluentAssertions.Types } public class PropertyInfoSelectorAssertions { - public PropertyInfoSelectorAssertions(params System.Reflection.PropertyInfo[] properties) { } + public PropertyInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.PropertyInfo[] properties) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectProperties { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) @@ -2360,7 +2336,7 @@ namespace FluentAssertions.Types } public class TypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public TypeAssertions(System.Type type) { } + public TypeAssertions(System.Type type, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Type expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string because = "", params object[] becauseArgs) { } @@ -2478,7 +2454,7 @@ namespace FluentAssertions.Types } public class TypeSelectorAssertions { - public TypeSelectorAssertions(params System.Type[] types) { } + public TypeSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Type[] types) { } public System.Collections.Generic.IEnumerable Subject { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } @@ -2509,7 +2485,7 @@ namespace FluentAssertions.Xml { public class XAttributeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XAttributeAssertions(System.Xml.Linq.XAttribute attribute) { } + public XAttributeAssertions(System.Xml.Linq.XAttribute attribute, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XAttribute expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string expected, string because = "", params object[] becauseArgs) { } @@ -2517,7 +2493,7 @@ namespace FluentAssertions.Xml } public class XDocumentAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XDocumentAssertions(System.Xml.Linq.XDocument document) { } + public XDocumentAssertions(System.Xml.Linq.XDocument document, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } @@ -2532,7 +2508,7 @@ namespace FluentAssertions.Xml } public class XElementAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XElementAssertions(System.Xml.Linq.XElement xElement) { } + public XElementAssertions(System.Xml.Linq.XElement xElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } @@ -2548,7 +2524,7 @@ namespace FluentAssertions.Xml } public class XmlElementAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlElementAssertions(System.Xml.XmlElement xmlElement) { } + public XmlElementAssertions(System.Xml.XmlElement xmlElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint HaveAttribute(string expectedName, string expectedValue, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveAttributeWithNamespace(string expectedName, string expectedNamespace, string expectedValue, string because = "", params object[] becauseArgs) { } @@ -2558,13 +2534,13 @@ namespace FluentAssertions.Xml } public class XmlNodeAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(System.Xml.XmlNode xmlNode) { } + public XmlNodeAssertions(System.Xml.XmlNode xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class XmlNodeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Xml.XmlNode where TAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(TSubject xmlNode) { } + public XmlNodeAssertions(TSubject xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.XmlNode expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeEquivalentTo(System.Xml.XmlNode unexpected, string because = "", params object[] becauseArgs) { } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt index 24ab2600ca..ded4744261 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt @@ -15,12 +15,14 @@ namespace FluentAssertions public AndConstraint(TParent parent) { } public TParent And { get; } } - public class AndWhichConstraint : FluentAssertions.AndConstraint + public class AndWhichConstraint : FluentAssertions.AndConstraint { - public AndWhichConstraint(TParentConstraint parentConstraint, System.Collections.Generic.IEnumerable matchedConstraint) { } - public AndWhichConstraint(TParentConstraint parentConstraint, TMatchedElement matchedConstraint) { } - public TMatchedElement Subject { get; } - public TMatchedElement Which { get; } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects) { } + public AndWhichConstraint(TParent parent, TSubject subject) { } + public AndWhichConstraint(TParent parent, System.Collections.Generic.IEnumerable subjects, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix) { } + public AndWhichConstraint(TParent parent, TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain, string pathPostfix = "") { } + public TSubject Subject { get; } + public TSubject Which { get; } } public static class AssertionExtensions { @@ -376,18 +378,18 @@ namespace FluentAssertions.Collections { public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions, T, FluentAssertions.Collections.GenericCollectionAssertions> { - public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public GenericCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericCollectionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.GenericCollectionAssertions { - public GenericCollectionAssertions(TCollection actualValue) { } + public GenericCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint AllBeAssignableTo(System.Type expectedType, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint> AllBeAssignableTo(string because = "", params object[] becauseArgs) { } @@ -461,7 +463,7 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint NotBeSubsetOf(System.Collections.Generic.IEnumerable unexpectedSuperset, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Collections.Generic.IEnumerable unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContain(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndWhichConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotContain(T unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainEquivalentOf(TExpectation unexpected, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotContainInConsecutiveOrder(params T[] unexpected) { } @@ -492,13 +494,13 @@ namespace FluentAssertions.Collections public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericDictionaryAssertions> where TCollection : System.Collections.Generic.IEnumerable> { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GenericDictionaryAssertions : FluentAssertions.Collections.GenericCollectionAssertions, TAssertions> where TCollection : System.Collections.Generic.IEnumerable> where TAssertions : FluentAssertions.Collections.GenericDictionaryAssertions { - public GenericDictionaryAssertions(TCollection keyValuePairs) { } + public GenericDictionaryAssertions(TCollection keyValuePairs, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } @@ -510,8 +512,8 @@ namespace FluentAssertions.Collections public FluentAssertions.AndConstraint ContainKeys(params TKey[] expected) { } public FluentAssertions.AndConstraint ContainKeys(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint ContainValue(TValue expected, string because = "", params object[] becauseArgs) { } - public FluentAssertions.AndConstraint ContainValues(params TValue[] expected) { } - public FluentAssertions.AndConstraint ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint> ContainValues(params TValue[] expected) { } + public FluentAssertions.AndWhichConstraint> ContainValues(System.Collections.Generic.IEnumerable expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Equal(T expected, string because = "", params object[] becauseArgs) where T : System.Collections.Generic.IEnumerable> { } public FluentAssertions.AndConstraint NotContain(params System.Collections.Generic.KeyValuePair[] items) { } @@ -529,18 +531,18 @@ namespace FluentAssertions.Collections } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> { - public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue) { } + public StringCollectionAssertions(System.Collections.Generic.IEnumerable actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.StringCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.StringCollectionAssertions { - public StringCollectionAssertions(TCollection actualValue) { } + public StringCollectionAssertions(TCollection actualValue, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint AllBe(string expectation, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint AllBe(string expectation, System.Func, FluentAssertions.Equivalency.EquivalencyOptions> config, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(params string[] expectation) { } @@ -553,18 +555,18 @@ namespace FluentAssertions.Collections } public class SubsequentOrderingAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions, T, FluentAssertions.Collections.SubsequentOrderingAssertions> { - public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingAssertions(System.Collections.Generic.IEnumerable actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions> where TCollection : System.Collections.Generic.IEnumerable { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SubsequentOrderingGenericCollectionAssertions : FluentAssertions.Collections.GenericCollectionAssertions where TCollection : System.Collections.Generic.IEnumerable where TAssertions : FluentAssertions.Collections.SubsequentOrderingGenericCollectionAssertions { - public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable) { } + public SubsequentOrderingGenericCollectionAssertions(TCollection actualValue, System.Linq.IOrderedEnumerable previousOrderedEnumerable, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInAscendingOrder(System.Linq.Expressions.Expression> propertyExpression, System.Collections.Generic.IComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint> ThenBeInDescendingOrder(System.Linq.Expressions.Expression> propertyExpression, string because = "", params object[] becauseArgs) { } @@ -705,7 +707,7 @@ namespace FluentAssertions.Equivalency { protected EquivalencyStep() { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } - protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested); + protected abstract FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator); } public class EquivalencyValidationContext : FluentAssertions.Equivalency.IEquivalencyValidationContext { @@ -797,7 +799,7 @@ namespace FluentAssertions.Equivalency } public interface IMemberMatchingRule { - FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options); + FluentAssertions.Equivalency.IMember Match(FluentAssertions.Equivalency.IMember expectedMember, object subject, FluentAssertions.Equivalency.INode parent, FluentAssertions.Equivalency.IEquivalencyOptions options, FluentAssertions.Execution.AssertionChain assertionChain); } public interface IMemberSelectionRule { @@ -959,7 +961,7 @@ namespace FluentAssertions.Equivalency.Steps { public class AssertionRuleEquivalencyStep : FluentAssertions.Equivalency.IEquivalencyStep { - public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertion) { } + public AssertionRuleEquivalencyStep(System.Linq.Expressions.Expression> predicate, System.Action> assertionAction) { } public FluentAssertions.Equivalency.EquivalencyResult Handle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency valueChildNodes) { } public override string ToString() { } } @@ -972,7 +974,7 @@ namespace FluentAssertions.Equivalency.Steps public class DictionaryEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public DictionaryEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class EnumEqualityStep : FluentAssertions.Equivalency.IEquivalencyStep { @@ -1033,17 +1035,17 @@ namespace FluentAssertions.Equivalency.Steps public class XAttributeEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XAttributeEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XDocumentEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XDocumentEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } public class XElementEquivalencyStep : FluentAssertions.Equivalency.EquivalencyStep { public XElementEquivalencyStep() { } - protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nested) { } + protected override FluentAssertions.Equivalency.EquivalencyResult OnHandle(FluentAssertions.Equivalency.Comparands comparands, FluentAssertions.Equivalency.IEquivalencyValidationContext context, FluentAssertions.Equivalency.IValidateChildNodeEquivalency nestedValidator) { } } } namespace FluentAssertions.Equivalency.Tracing @@ -1073,7 +1075,7 @@ namespace FluentAssertions.Events { public class EventAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> { - protected EventAssertions(FluentAssertions.Events.IMonitor monitor) { } + protected EventAssertions(FluentAssertions.Events.IMonitor monitor, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.Events.IMonitor Monitor { get; } public void NotRaise(string eventName, string because = "", params object[] becauseArgs) { } @@ -1119,76 +1121,65 @@ namespace FluentAssertions.Events } namespace FluentAssertions.Execution { + public sealed class AssertionChain + { + public string CallerIdentifier { get; } + public bool HasOverriddenCallerIdentifier { get; } + public bool PreviousAssertionSucceeded { get; } + public bool Succeeded { get; } + public FluentAssertions.Execution.AssertionChain UsingLineBreaks { get; } + public void AddPreFormattedFailure(string failure) { } + public void AddReportable(string key, System.Func getValue) { } + public void AddReportable(string key, string value) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(FluentAssertions.Execution.Reason reason) { } + public FluentAssertions.Execution.AssertionChain BecauseOf(string because, params object[] becauseArgs) { } + public FluentAssertions.Execution.Continuation FailWith(System.Func getFailureReason) { } + public FluentAssertions.Execution.Continuation FailWith(string message) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } + public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } + public FluentAssertions.Execution.AssertionChain ForCondition(bool condition) { } + public FluentAssertions.Execution.AssertionChain ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } + public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } + public void OverrideCallerIdentifier(System.Func getCallerIdentifier) { } + public void ReuseOnce() { } + public FluentAssertions.Execution.AssertionChain WithCallerPostfix(string postfix) { } + public FluentAssertions.Execution.AssertionChain WithCallerPrefix(string prefix) { } + public FluentAssertions.Execution.AssertionChain WithDefaultIdentifier(string identifier) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, System.Action chain) { } + public FluentAssertions.Execution.Continuation WithExpectation(string message, object arg1, object arg2, System.Action chain) { } + public FluentAssertions.Execution.AssertionChain WithReportable(string name, System.Func content) { } + public static FluentAssertions.Execution.AssertionChain GetOrCreate() { } + } [System.Serializable] public class AssertionFailedException : System.Exception { public AssertionFailedException(string message) { } protected AssertionFailedException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } - public sealed class AssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable + public sealed class AssertionScope : System.IDisposable { public AssertionScope() { } public AssertionScope(FluentAssertions.Execution.IAssertionStrategy assertionStrategy) { } - public AssertionScope(System.Lazy context) { } - public AssertionScope(string context) { } - public string CallerIdentity { get; } - public System.Lazy Context { get; set; } + public AssertionScope(System.Func name) { } + public AssertionScope(string name) { } public FluentAssertions.Formatting.FormattingOptions FormattingOptions { get; } - public FluentAssertions.Execution.AssertionScope UsingLineBreaks { get; } + public System.Func Name { get; } public static FluentAssertions.Execution.AssertionScope Current { get; } - public void AddNonReportable(string key, object value) { } public void AddPreFormattedFailure(string formattedFailureMessage) { } - public void AddReportable(string key, System.Func valueFunc) { } - public void AddReportable(string key, string value) { } public void AppendTracing(string tracingBlock) { } - public void AssumeSingleCaller() { } - public FluentAssertions.Execution.AssertionScope BecauseOf(FluentAssertions.Execution.Reason reason) { } - public FluentAssertions.Execution.AssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } public string[] Discard() { } public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.AssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.AssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public T Get(string key) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } public bool HasFailures() { } - public FluentAssertions.Execution.AssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.AssertionScope WithExpectation(string message, params object[] args) { } } public class Continuation { - public FluentAssertions.Execution.IAssertionScope Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.Continuation continuation) { } + public FluentAssertions.Execution.AssertionChain Then { get; } } public class ContinuationOfGiven { + public bool Succeeded { get; } public FluentAssertions.Execution.GivenSelector Then { get; } - public static bool op_Implicit(FluentAssertions.Execution.ContinuationOfGiven continuationOfGiven) { } - } - public sealed class ContinuedAssertionScope : FluentAssertions.Execution.IAssertionScope, System.IDisposable - { - public FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - public FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs) { } - public FluentAssertions.Execution.Continuation ClearExpectation() { } - public string[] Discard() { } - public void Dispose() { } - public FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc) { } - public FluentAssertions.Execution.Continuation FailWith(string message) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders) { } - public FluentAssertions.Execution.Continuation FailWith(string message, params object[] args) { } - public FluentAssertions.Execution.IAssertionScope ForCondition(bool condition) { } - public FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences) { } - public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } - public FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier) { } - public FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args) { } - } - public static class Execute - { - public static FluentAssertions.Execution.AssertionScope Assertion { get; } } public class FailReason { @@ -1198,29 +1189,13 @@ namespace FluentAssertions.Execution } public class GivenSelector { - public FluentAssertions.Execution.ContinuationOfGiven ClearExpectation() { } + public bool Succeeded { get; } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params System.Func[] args) { } public FluentAssertions.Execution.ContinuationOfGiven FailWith(string message, params object[] args) { } public FluentAssertions.Execution.GivenSelector ForCondition(System.Func predicate) { } public FluentAssertions.Execution.GivenSelector Given(System.Func selector) { } } - public interface IAssertionScope : System.IDisposable - { - FluentAssertions.Execution.IAssertionScope UsingLineBreaks { get; } - FluentAssertions.Execution.IAssertionScope BecauseOf(string because, params object[] becauseArgs); - FluentAssertions.Execution.Continuation ClearExpectation(); - string[] Discard(); - FluentAssertions.Execution.Continuation FailWith(System.Func failReasonFunc); - FluentAssertions.Execution.Continuation FailWith(string message); - FluentAssertions.Execution.Continuation FailWith(string message, params System.Func[] argProviders); - FluentAssertions.Execution.Continuation FailWith(string message, params object[] args); - FluentAssertions.Execution.IAssertionScope ForCondition(bool condition); - FluentAssertions.Execution.IAssertionScope ForConstraint(FluentAssertions.OccurrenceConstraint constraint, int actualOccurrences); - FluentAssertions.Execution.GivenSelector Given(System.Func selector); - FluentAssertions.Execution.IAssertionScope WithDefaultIdentifier(string identifier); - FluentAssertions.Execution.IAssertionScope WithExpectation(string message, params object[] args); - } public interface IAssertionStrategy { System.Collections.Generic.IEnumerable FailureMessages { get; } @@ -1463,6 +1438,12 @@ namespace FluentAssertions.Formatting public MaxLinesExceededException(string message) { } public MaxLinesExceededException(string message, System.Exception innerException) { } } + public class MethodInfoFormatter : FluentAssertions.Formatting.IValueFormatter + { + public MethodInfoFormatter() { } + public bool CanHandle(object value) { } + public void Format(object value, FluentAssertions.Formatting.FormattedObjectGraph formattedGraph, FluentAssertions.Formatting.FormattingContext context, FluentAssertions.Formatting.FormatChild formatChild) { } + } public class MultidimensionalArrayFormatter : FluentAssertions.Formatting.IValueFormatter { public MultidimensionalArrayFormatter() { } @@ -1569,12 +1550,12 @@ namespace FluentAssertions.Numeric { public class ComparableTypeAssertions : FluentAssertions.Numeric.ComparableTypeAssertions> { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class ComparableTypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, TAssertions> where TAssertions : FluentAssertions.Numeric.ComparableTypeAssertions { - public ComparableTypeAssertions(System.IComparable value) { } + public ComparableTypeAssertions(System.IComparable value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(TExpectation expectation, string because = "", params object[] becauseArgs) { } @@ -1594,13 +1575,13 @@ namespace FluentAssertions.Numeric public class NullableNumericAssertions : FluentAssertions.Numeric.NullableNumericAssertions> where T : struct, System.IComparable { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableNumericAssertions : FluentAssertions.Numeric.NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NullableNumericAssertions { - public NullableNumericAssertions(T? value) { } + public NullableNumericAssertions(T? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Match(System.Linq.Expressions.Expression> predicate, string because = "", params object[] becauseArgs) { } @@ -1610,13 +1591,14 @@ namespace FluentAssertions.Numeric public class NumericAssertions : FluentAssertions.Numeric.NumericAssertions> where T : struct, System.IComparable { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NumericAssertions where T : struct, System.IComparable where TAssertions : FluentAssertions.Numeric.NumericAssertions { - public NumericAssertions(T value) { } + public NumericAssertions(T value, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } public T? Subject { get; } public FluentAssertions.AndConstraint Be(T expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(T? expected, string because = "", params object[] becauseArgs) { } @@ -1642,12 +1624,12 @@ namespace FluentAssertions.Primitives { public class BooleanAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class BooleanAssertions where TAssertions : FluentAssertions.Primitives.BooleanAssertions { - public BooleanAssertions(bool? value) { } + public BooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public bool? Subject { get; } public FluentAssertions.AndConstraint Be(bool expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeFalse(string because = "", params object[] becauseArgs) { } @@ -1658,12 +1640,12 @@ namespace FluentAssertions.Primitives } public class DateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - public DateTimeAssertions(System.DateTime? value) { } + public DateTimeAssertions(System.DateTime? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTime? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTime expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTime? expected, string because = "", params object[] becauseArgs) { } @@ -1708,12 +1690,12 @@ namespace FluentAssertions.Primitives } public class DateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - public DateTimeOffsetAssertions(System.DateTimeOffset? value) { } + public DateTimeOffsetAssertions(System.DateTimeOffset? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.DateTimeOffset? Subject { get; } public FluentAssertions.AndConstraint Be(System.DateTimeOffset expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(System.DateTimeOffset? expected, string because = "", params object[] becauseArgs) { } @@ -1763,7 +1745,7 @@ namespace FluentAssertions.Primitives public class DateTimeOffsetRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions { - protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeOffsetRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTimeOffset? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTimeOffset target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1771,7 +1753,7 @@ namespace FluentAssertions.Primitives public class DateTimeRangeAssertions where TAssertions : FluentAssertions.Primitives.DateTimeAssertions { - protected DateTimeRangeAssertions(TAssertions parentAssertions, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } + protected DateTimeRangeAssertions(TAssertions parentAssertions, FluentAssertions.Execution.AssertionChain assertionChain, System.DateTime? subject, FluentAssertions.Primitives.TimeSpanCondition condition, System.TimeSpan timeSpan) { } public FluentAssertions.AndConstraint After(System.DateTime target, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Before(System.DateTime target, string because = "", params object[] becauseArgs) { } public override bool Equals(object obj) { } @@ -1779,13 +1761,13 @@ namespace FluentAssertions.Primitives public class EnumAssertions : FluentAssertions.Primitives.EnumAssertions> where TEnum : struct, System.Enum { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.EnumAssertions { - public EnumAssertions(TEnum subject) { } + public EnumAssertions(TEnum subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public TEnum? Subject { get; } public FluentAssertions.AndConstraint Be(TEnum expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TEnum? expected, string because = "", params object[] becauseArgs) { } @@ -1812,12 +1794,12 @@ namespace FluentAssertions.Primitives } public class GuidAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class GuidAssertions where TAssertions : FluentAssertions.Primitives.GuidAssertions { - public GuidAssertions(System.Guid? value) { } + public GuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.Guid? Subject { get; } public FluentAssertions.AndConstraint Be(System.Guid expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } @@ -1829,12 +1811,12 @@ namespace FluentAssertions.Primitives } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + public HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class HttpResponseMessageAssertions : FluentAssertions.Primitives.ObjectAssertions where TAssertions : FluentAssertions.Primitives.HttpResponseMessageAssertions { - protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value) { } + protected HttpResponseMessageAssertions(System.Net.Http.HttpResponseMessage value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeRedirection(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeSuccessful(string because = "", params object[] becauseArgs) { } @@ -1846,12 +1828,12 @@ namespace FluentAssertions.Primitives } public class NullableBooleanAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableBooleanAssertions : FluentAssertions.Primitives.BooleanAssertions where TAssertions : FluentAssertions.Primitives.NullableBooleanAssertions { - public NullableBooleanAssertions(bool? value) { } + public NullableBooleanAssertions(bool? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(bool? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1863,12 +1845,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeAssertions : FluentAssertions.Primitives.DateTimeAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeAssertions { - public NullableDateTimeAssertions(System.DateTime? expected) { } + public NullableDateTimeAssertions(System.DateTime? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1876,12 +1858,12 @@ namespace FluentAssertions.Primitives } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableDateTimeOffsetAssertions : FluentAssertions.Primitives.DateTimeOffsetAssertions where TAssertions : FluentAssertions.Primitives.NullableDateTimeOffsetAssertions { - public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected) { } + public NullableDateTimeOffsetAssertions(System.DateTimeOffset? expected, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1890,13 +1872,13 @@ namespace FluentAssertions.Primitives public class NullableEnumAssertions : FluentAssertions.Primitives.NullableEnumAssertions> where TEnum : struct, System.Enum { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableEnumAssertions : FluentAssertions.Primitives.EnumAssertions where TEnum : struct, System.Enum where TAssertions : FluentAssertions.Primitives.NullableEnumAssertions { - public NullableEnumAssertions(TEnum? subject) { } + public NullableEnumAssertions(TEnum? subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint HaveValue(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint NotBeNull(string because = "", params object[] becauseArgs) { } @@ -1904,12 +1886,12 @@ namespace FluentAssertions.Primitives } public class NullableGuidAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableGuidAssertions : FluentAssertions.Primitives.GuidAssertions where TAssertions : FluentAssertions.Primitives.NullableGuidAssertions { - public NullableGuidAssertions(System.Guid? value) { } + public NullableGuidAssertions(System.Guid? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.Guid? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1918,12 +1900,12 @@ namespace FluentAssertions.Primitives } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class NullableSimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.NullableSimpleTimeSpanAssertions { - public NullableSimpleTimeSpanAssertions(System.TimeSpan? value) { } + public NullableSimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(System.TimeSpan? expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeNull(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string because = "", params object[] becauseArgs) { } @@ -1932,7 +1914,7 @@ namespace FluentAssertions.Primitives } public class ObjectAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(object value) { } + public ObjectAssertions(object value, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint Be(TExpectation expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeOneOf(System.Collections.Generic.IEnumerable validValues, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBe(TExpectation unexpected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -1940,7 +1922,7 @@ namespace FluentAssertions.Primitives public class ObjectAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ObjectAssertions { - public ObjectAssertions(TSubject value) { } + public ObjectAssertions(TSubject value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(TSubject expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(TSubject expected, System.Collections.Generic.IEqualityComparer comparer, string because = "", params object[] becauseArgs) { } @@ -1958,7 +1940,8 @@ namespace FluentAssertions.Primitives public abstract class ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - protected ReferenceTypeAssertions(TSubject subject) { } + protected ReferenceTypeAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FluentAssertions.Execution.AssertionChain CurrentAssertionChain { get; } protected abstract string Identifier { get; } public TSubject Subject { get; } public FluentAssertions.AndConstraint BeAssignableTo(System.Type type, string because = "", params object[] becauseArgs) { } @@ -1982,12 +1965,12 @@ namespace FluentAssertions.Primitives } public class SimpleTimeSpanAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class SimpleTimeSpanAssertions where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { - public SimpleTimeSpanAssertions(System.TimeSpan? value) { } + public SimpleTimeSpanAssertions(System.TimeSpan? value, FluentAssertions.Execution.AssertionChain assertionChain) { } public System.TimeSpan? Subject { get; } public FluentAssertions.AndConstraint Be(System.TimeSpan expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan nearbyTime, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } @@ -2003,12 +1986,12 @@ namespace FluentAssertions.Primitives } public class StringAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StringAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TAssertions : FluentAssertions.Primitives.StringAssertions { - public StringAssertions(string value) { } + public StringAssertions(string value, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(string expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEmpty(string because = "", params object[] becauseArgs) { } @@ -2084,8 +2067,8 @@ namespace FluentAssertions.Specialized { public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } @@ -2095,7 +2078,7 @@ namespace FluentAssertions.Specialized where TTask : System.Threading.Tasks.Task where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + protected AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } public System.Threading.Tasks.Task> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) @@ -2121,7 +2104,7 @@ namespace FluentAssertions.Specialized where TDelegate : System.Delegate where TAssertions : FluentAssertions.Specialized.DelegateAssertions { - protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } + protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } protected abstract void InvokeSubject(); public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } @@ -2133,7 +2116,7 @@ namespace FluentAssertions.Specialized public class ExceptionAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions, FluentAssertions.Specialized.ExceptionAssertions> where TException : System.Exception { - public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions) { } + public ExceptionAssertions(System.Collections.Generic.IEnumerable exceptions, FluentAssertions.Execution.AssertionChain assertionChain) { } public TException And { get; } protected override string Identifier { get; } public TException Which { get; } @@ -2155,7 +2138,7 @@ namespace FluentAssertions.Specialized } public class ExecutionTimeAssertions { - public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime) { } + public ExecutionTimeAssertions(FluentAssertions.Specialized.ExecutionTime executionTime, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint BeCloseTo(System.TimeSpan expectedDuration, System.TimeSpan precision, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThan(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeGreaterThanOrEqualTo(System.TimeSpan minDuration, string because = "", params object[] becauseArgs) { } @@ -2165,8 +2148,8 @@ namespace FluentAssertions.Specialized } public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } @@ -2174,8 +2157,8 @@ namespace FluentAssertions.Specialized } public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } @@ -2191,8 +2174,8 @@ namespace FluentAssertions.Specialized } public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain) { } + public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } @@ -2204,8 +2187,8 @@ namespace FluentAssertions.Specialized } public class TaskCompletionSourceAssertions : FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase { - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs) { } - public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Common.IClock clock) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain) { } + public TaskCompletionSourceAssertions(System.Threading.Tasks.TaskCompletionSource tcs, FluentAssertions.Execution.AssertionChain assertionChain, FluentAssertions.Common.IClock clock) { } public System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task>> NotCompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } @@ -2214,25 +2197,25 @@ namespace FluentAssertions.Streams { public class BufferedStreamAssertions : FluentAssertions.Streams.BufferedStreamAssertions { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } + public BufferedStreamAssertions(System.IO.BufferedStream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class BufferedStreamAssertions : FluentAssertions.Streams.StreamAssertions where TAssertions : FluentAssertions.Streams.BufferedStreamAssertions { - public BufferedStreamAssertions(System.IO.BufferedStream stream) { } + public BufferedStreamAssertions(System.IO.BufferedStream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint HaveBufferSize(int expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotHaveBufferSize(int unexpected, string because = "", params object[] becauseArgs) { } } public class StreamAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(System.IO.Stream stream) { } + public StreamAssertions(System.IO.Stream stream, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class StreamAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.IO.Stream where TAssertions : FluentAssertions.Streams.StreamAssertions { - public StreamAssertions(TSubject stream) { } + public StreamAssertions(TSubject stream, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeReadOnly(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } @@ -2258,7 +2241,7 @@ namespace FluentAssertions.Types } public class AssemblyAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public AssemblyAssertions(System.Reflection.Assembly assembly) { } + public AssemblyAssertions(System.Reflection.Assembly assembly, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeSignedWithPublicKey(string publicKey, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeUnsigned(string because = "", params object[] becauseArgs) { } @@ -2268,15 +2251,17 @@ namespace FluentAssertions.Types } public class ConstructorInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo) { } + public ConstructorInfoAssertions(System.Reflection.ConstructorInfo constructorInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } } public abstract class MemberInfoAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Reflection.MemberInfo where TAssertions : FluentAssertions.Types.MemberInfoAssertions { - protected MemberInfoAssertions(TSubject subject) { } + protected MemberInfoAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected virtual string SubjectDescription { get; } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } public FluentAssertions.AndWhichConstraint, TAttribute> BeDecoratedWith(System.Linq.Expressions.Expression> isMatchingAttributePredicate, string because = "", params object[] becauseArgs) @@ -2290,14 +2275,15 @@ namespace FluentAssertions.Types where TSubject : System.Reflection.MethodBase where TAssertions : FluentAssertions.Types.MethodBaseAssertions { - protected MethodBaseAssertions(TSubject subject) { } + protected MethodBaseAssertions(TSubject subject, FluentAssertions.Execution.AssertionChain assertionChain) { } public FluentAssertions.AndConstraint HaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotHaveAccessModifier(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } } public class MethodInfoAssertions : FluentAssertions.Types.MethodBaseAssertions { - public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo) { } + public MethodInfoAssertions(System.Reflection.MethodInfo methodInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeAsync(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeAsync(string because = "", params object[] becauseArgs) { } @@ -2340,7 +2326,7 @@ namespace FluentAssertions.Types } public class MethodInfoSelectorAssertions { - public MethodInfoSelectorAssertions(params System.Reflection.MethodInfo[] methods) { } + public MethodInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.MethodInfo[] methods) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectMethods { get; } public FluentAssertions.AndConstraint Be(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } @@ -2361,8 +2347,9 @@ namespace FluentAssertions.Types } public class PropertyInfoAssertions : FluentAssertions.Types.MemberInfoAssertions { - public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo) { } + public PropertyInfoAssertions(System.Reflection.PropertyInfo propertyInfo, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } + protected override string SubjectDescription { get; } public FluentAssertions.AndConstraint BeReadable(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeReadable(FluentAssertions.Common.CSharpAccessModifier accessModifier, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeVirtual(string because = "", params object[] becauseArgs) { } @@ -2403,7 +2390,7 @@ namespace FluentAssertions.Types } public class PropertyInfoSelectorAssertions { - public PropertyInfoSelectorAssertions(params System.Reflection.PropertyInfo[] properties) { } + public PropertyInfoSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Reflection.PropertyInfo[] properties) { } protected string Context { get; } public System.Collections.Generic.IEnumerable SubjectProperties { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) @@ -2418,7 +2405,7 @@ namespace FluentAssertions.Types } public class TypeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public TypeAssertions(System.Type type) { } + public TypeAssertions(System.Type type, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Type expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint Be(string because = "", params object[] becauseArgs) { } @@ -2536,7 +2523,7 @@ namespace FluentAssertions.Types } public class TypeSelectorAssertions { - public TypeSelectorAssertions(params System.Type[] types) { } + public TypeSelectorAssertions(FluentAssertions.Execution.AssertionChain assertionChain, params System.Type[] types) { } public System.Collections.Generic.IEnumerable Subject { get; } public FluentAssertions.AndConstraint BeDecoratedWith(string because = "", params object[] becauseArgs) where TAttribute : System.Attribute { } @@ -2567,7 +2554,7 @@ namespace FluentAssertions.Xml { public class XAttributeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XAttributeAssertions(System.Xml.Linq.XAttribute attribute) { } + public XAttributeAssertions(System.Xml.Linq.XAttribute attribute, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XAttribute expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveValue(string expected, string because = "", params object[] becauseArgs) { } @@ -2575,7 +2562,7 @@ namespace FluentAssertions.Xml } public class XDocumentAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XDocumentAssertions(System.Xml.Linq.XDocument document) { } + public XDocumentAssertions(System.Xml.Linq.XDocument document, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XDocument expected, string because = "", params object[] becauseArgs) { } @@ -2590,7 +2577,7 @@ namespace FluentAssertions.Xml } public class XElementAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions { - public XElementAssertions(System.Xml.Linq.XElement xElement) { } + public XElementAssertions(System.Xml.Linq.XElement xElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint Be(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.Linq.XElement expected, string because = "", params object[] becauseArgs) { } @@ -2606,7 +2593,7 @@ namespace FluentAssertions.Xml } public class XmlElementAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlElementAssertions(System.Xml.XmlElement xmlElement) { } + public XmlElementAssertions(System.Xml.XmlElement xmlElement, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint HaveAttribute(string expectedName, string expectedValue, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint HaveAttributeWithNamespace(string expectedName, string expectedNamespace, string expectedValue, string because = "", params object[] becauseArgs) { } @@ -2616,13 +2603,13 @@ namespace FluentAssertions.Xml } public class XmlNodeAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(System.Xml.XmlNode xmlNode) { } + public XmlNodeAssertions(System.Xml.XmlNode xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } } public class XmlNodeAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions where TSubject : System.Xml.XmlNode where TAssertions : FluentAssertions.Xml.XmlNodeAssertions { - public XmlNodeAssertions(TSubject xmlNode) { } + public XmlNodeAssertions(TSubject xmlNode, FluentAssertions.Execution.AssertionChain assertionChain) { } protected override string Identifier { get; } public FluentAssertions.AndConstraint BeEquivalentTo(System.Xml.XmlNode expected, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint NotBeEquivalentTo(System.Xml.XmlNode unexpected, string because = "", params object[] becauseArgs) { } diff --git a/Tests/Benchmarks/Program.cs b/Tests/Benchmarks/Program.cs index 2dc62955e7..ff2fb9ab18 100644 --- a/Tests/Benchmarks/Program.cs +++ b/Tests/Benchmarks/Program.cs @@ -1,5 +1,4 @@ using System.Globalization; -using BenchmarkDotNet.Columns; using BenchmarkDotNet.Configs; using BenchmarkDotNet.Exporters.Csv; using BenchmarkDotNet.Reports; diff --git a/Tests/FluentAssertions.Equivalency.Specs/BasicSpecs.cs b/Tests/FluentAssertions.Equivalency.Specs/BasicSpecs.cs index 1936a02b89..b1c54dd353 100644 --- a/Tests/FluentAssertions.Equivalency.Specs/BasicSpecs.cs +++ b/Tests/FluentAssertions.Equivalency.Specs/BasicSpecs.cs @@ -599,8 +599,7 @@ public void When_asserting_equivalence_including_only_properties_it_should_not_m } [Fact] - public void - When_asserting_equivalence_of_objects_including_enumerables_it_should_print_the_failure_message_only_once() + public void When_asserting_equivalence_of_objects_including_enumerables_it_should_print_the_failure_message_only_once() { // Arrange var record = new { Member1 = "", Member2 = new[] { "", "" } }; diff --git a/Tests/FluentAssertions.Equivalency.Specs/CollectionSpecs.cs b/Tests/FluentAssertions.Equivalency.Specs/CollectionSpecs.cs index 64e036548d..27c74ec048 100644 --- a/Tests/FluentAssertions.Equivalency.Specs/CollectionSpecs.cs +++ b/Tests/FluentAssertions.Equivalency.Specs/CollectionSpecs.cs @@ -1467,8 +1467,7 @@ public void Can_force_strict_ordering_based_on_the_parent_type_of_an_unordered_c // Assert action.Should().Throw() - .WithMessage( - "*Expected*[0].UnorderedCollection*5 item(s)*empty collection*"); + .WithMessage("*Expected*[0].UnorderedCollection*5 item(s)*empty collection*"); } [Fact] diff --git a/Tests/FluentAssertions.Equivalency.Specs/DictionarySpecs.cs b/Tests/FluentAssertions.Equivalency.Specs/DictionarySpecs.cs index ea5f509b56..131efc6be3 100644 --- a/Tests/FluentAssertions.Equivalency.Specs/DictionarySpecs.cs +++ b/Tests/FluentAssertions.Equivalency.Specs/DictionarySpecs.cs @@ -570,6 +570,29 @@ public void act.Should().Throw("the types have different properties"); } + [Fact] + public void Can_compare_non_generic_directories_without_recursing() + { + // Arrange + var expected = new NonGenericDictionary + { + ["Key2"] = "Value2", + ["Key1"] = "Value1" + }; + + var subject = new NonGenericDictionary + { + ["Key1"] = "Value1", + ["Key3"] = "Value2" + }; + + // Act + Action act = () => subject.Should().BeEquivalentTo(expected, options => options.ExcludingNestedObjects()); + + // Assert + act.Should().Throw().WithMessage("Expected subject[\"Key2\"] to be \"Value2\", but found *"); + } + [Fact] public void When_asserting_equivalence_of_dictionaries_it_should_respect_the_declared_type() { diff --git a/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs b/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs index a4c7478d4a..af8beba7ce 100644 --- a/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs +++ b/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Linq; using System.Reflection; +using FluentAssertions.Execution; using FluentAssertions.Extensions; using JetBrains.Annotations; using Xunit; @@ -141,7 +142,8 @@ public void When_a_matching_rule_is_added_it_should_appear_in_the_exception_mess internal class ForeignKeyMatchingRule : IMemberMatchingRule { - public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options) + public IMember Match(IMember expectedMember, object subject, INode parent, IEquivalencyOptions options, + AssertionChain assertionChain) { string name = expectedMember.Name; @@ -296,7 +298,7 @@ public void When_equally_named_properties_are_both_incompatible_with_generic_typ // Assert act.Should().Throw() - .WithMessage("*Id*from subject*System.String*System.Double*Id*from expectation*System.String*System.Double*"); + .WithMessage("*Id*from subject*System.String*System.Double*"); } [Fact] @@ -335,13 +337,13 @@ public void When_property_of_subject_is_null_the_failure_message_should_not_comp Id = null as double?, }; - var other = new + var expectation = new { Id = "bar", }; // Act - Action act = () => subject.Should().BeEquivalentTo(other, + Action act = () => subject.Should().BeEquivalentTo(expectation, o => o .Using(c => c.Subject.Should().Be(c.Expectation)) .When(si => si.Path == "Id")); diff --git a/Tests/FluentAssertions.Specs/AssertionExtensions.cs b/Tests/FluentAssertions.Specs/AssertionExtensions.cs index 1e8936c211..add27b751a 100644 --- a/Tests/FluentAssertions.Specs/AssertionExtensions.cs +++ b/Tests/FluentAssertions.Specs/AssertionExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using FluentAssertions.Common; +using FluentAssertions.Execution; using FluentAssertions.Specialized; namespace FluentAssertions.Specs; @@ -11,33 +12,33 @@ internal static class AssertionExtensions public static NonGenericAsyncFunctionAssertions Should(this Func action, IClock clock) { - return new NonGenericAsyncFunctionAssertions(action, Extractor, clock); + return new NonGenericAsyncFunctionAssertions(action, Extractor, AssertionChain.GetOrCreate(), clock); } public static GenericAsyncFunctionAssertions Should(this Func> action, IClock clock) { - return new GenericAsyncFunctionAssertions(action, Extractor, clock); + return new GenericAsyncFunctionAssertions(action, Extractor, AssertionChain.GetOrCreate(), clock); } public static ActionAssertions Should(this Action action, IClock clock) { - return new ActionAssertions(action, Extractor, clock); + return new ActionAssertions(action, Extractor, AssertionChain.GetOrCreate(), clock); } public static FunctionAssertions Should(this Func func, IClock clock) { - return new FunctionAssertions(func, Extractor, clock); + return new FunctionAssertions(func, Extractor, AssertionChain.GetOrCreate(), clock); } public static TaskCompletionSourceAssertions Should(this TaskCompletionSource tcs, IClock clock) { - return new TaskCompletionSourceAssertions(tcs, clock); + return new TaskCompletionSourceAssertions(tcs, AssertionChain.GetOrCreate(), clock); } #if NET6_0_OR_GREATER public static TaskCompletionSourceAssertions Should(this TaskCompletionSource tcs, IClock clock) { - return new TaskCompletionSourceAssertions(tcs, clock); + return new TaskCompletionSourceAssertions(tcs, AssertionChain.GetOrCreate(), clock); } #endif diff --git a/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs b/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs index 9ee81a9033..4963fd2a99 100644 --- a/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs +++ b/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using FluentAssertions.Common; +using FluentAssertions.Execution; using FluentAssertions.Numeric; using FluentAssertions.Primitives; using FluentAssertions.Specialized; @@ -40,24 +41,24 @@ private static bool OverridesEquals(Type t) public static TheoryData ClassesWithGuardEquals => new() { - new ObjectAssertions(default), - new BooleanAssertions(default), - new DateTimeAssertions(default), - new DateTimeRangeAssertions(default, default, default, default), - new DateTimeOffsetAssertions(default), - new DateTimeOffsetRangeAssertions(default, default, default, default), - new ExecutionTimeAssertions(new ExecutionTime(() => { }, () => new StopwatchTimer())), - new GuidAssertions(default), - new MethodInfoSelectorAssertions(), - new NumericAssertions>(default), - new PropertyInfoSelectorAssertions(), - new SimpleTimeSpanAssertions(default), - new TaskCompletionSourceAssertions(default), - new TypeSelectorAssertions(), - new EnumAssertions>(default), + new ObjectAssertions(default, AssertionChain.GetOrCreate()), + new BooleanAssertions(default, AssertionChain.GetOrCreate()), + new DateTimeAssertions(default, AssertionChain.GetOrCreate()), + new DateTimeRangeAssertions(default, AssertionChain.GetOrCreate(), default, default, default), + new DateTimeOffsetAssertions(default, AssertionChain.GetOrCreate()), + new DateTimeOffsetRangeAssertions(default, AssertionChain.GetOrCreate(), default, default, default), + new ExecutionTimeAssertions(new ExecutionTime(() => { }, () => new StopwatchTimer()), AssertionChain.GetOrCreate()), + new GuidAssertions(default, AssertionChain.GetOrCreate()), + new MethodInfoSelectorAssertions(AssertionChain.GetOrCreate()), + new NumericAssertions>(default, AssertionChain.GetOrCreate()), + new PropertyInfoSelectorAssertions(AssertionChain.GetOrCreate()), + new SimpleTimeSpanAssertions(default, AssertionChain.GetOrCreate()), + new TaskCompletionSourceAssertions(default, AssertionChain.GetOrCreate()), + new TypeSelectorAssertions(AssertionChain.GetOrCreate()), + new EnumAssertions>(default, AssertionChain.GetOrCreate()), #if NET6_0_OR_GREATER - new DateOnlyAssertions(default), - new TimeOnlyAssertions(default), + new DateOnlyAssertions(default, AssertionChain.GetOrCreate()), + new TimeOnlyAssertions(default, AssertionChain.GetOrCreate()), #endif }; @@ -163,7 +164,7 @@ public void Should_methods_have_a_matching_overload_to_guard_against_chaining_an public void Should_methods_returning_reference_type_assertions_are_annotated_with_not_null_attribute(MethodInfo method) { var notNullAttribute = method.GetParameters().Single().GetCustomAttribute(); - notNullAttribute.Should().NotBeNull(); + notNullAttribute.Should().NotBeNull($"because {method} should be annotated with [NotNull]"); } [Theory] @@ -171,7 +172,7 @@ public void Should_methods_returning_reference_type_assertions_are_annotated_wit public void Should_methods_not_returning_reference_type_assertions_are_not_annotated_with_not_null_attribute(MethodInfo method) { var notNullAttribute = method.GetParameters().Single().GetCustomAttribute(); - notNullAttribute.Should().BeNull(); + notNullAttribute.Should().BeNull($"because {method} should NOT be annotated with [NotNull]"); } public static IEnumerable GetShouldMethods(bool referenceTypes) diff --git a/Tests/FluentAssertions.Specs/AssertionFailureSpecs.cs b/Tests/FluentAssertions.Specs/AssertionFailureSpecs.cs index ea81a04d7a..857253db82 100644 --- a/Tests/FluentAssertions.Specs/AssertionFailureSpecs.cs +++ b/Tests/FluentAssertions.Specs/AssertionFailureSpecs.cs @@ -1,6 +1,5 @@ using System; using FluentAssertions.Execution; -using FluentAssertions.Primitives; using Xunit; using Xunit.Sdk; @@ -70,23 +69,15 @@ public void When_reason_does_not_start_with_because_but_is_prefixed_with_blanks_ .WithMessage("Expected it to fail\r\nbecause AssertionsTestSubClass should always fail."); } - internal class AssertionsTestSubClass : ReferenceTypeAssertions + internal class AssertionsTestSubClass { + private readonly AssertionChain assertionChain = AssertionChain.GetOrCreate(); + public void AssertFail(string because, params object[] becauseArgs) { - Execute.Assertion + assertionChain .BecauseOf(because, becauseArgs) .FailWith("Expected it to fail{reason}"); } - - protected override string Identifier - { - get { return "test"; } - } - - public AssertionsTestSubClass() - : base(null) - { - } } } diff --git a/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs b/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs index 0ffac09bcd..a931266d0b 100644 --- a/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs +++ b/Tests/FluentAssertions.Specs/AssertionOptionsSpecs.cs @@ -421,7 +421,7 @@ internal class MyEquivalencyStep : IEquivalencyStep public EquivalencyResult Handle(Comparands comparands, IEquivalencyValidationContext context, IValidateChildNodeEquivalency valueChildNodes) { - Execute.Assertion.FailWith(GetType().FullName); + AssertionChain.GetOrCreate().For(context).FailWith(GetType().FullName); return EquivalencyResult.EquivalencyProven; } diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeEquivalentTo.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeEquivalentTo.cs index 6f4688bdce..60a28858db 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeEquivalentTo.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.BeEquivalentTo.cs @@ -363,11 +363,11 @@ public void When_asserting_collections_not_to_be_equivalent_with_options_but_sub public void Default_immutable_array_should_not_be_equivalent_to_initialized_immutable_array() { // Arrange - ImmutableArray collection = default; - ImmutableArray collection1 = ImmutableArray.Create("a", "b", "c"); + ImmutableArray subject = default; + ImmutableArray expectation = ImmutableArray.Create("a", "b", "c"); // Act / Assert - collection.Should().NotBeEquivalentTo(collection1); + subject.Should().NotBeEquivalentTo(expectation); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.Contain.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.Contain.cs index 4c95e2abc2..4fb71e62ee 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.Contain.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.Contain.cs @@ -104,7 +104,8 @@ public void Action act = () => { using var _ = new AssertionScope(); - collection.Should().Contain([4]).And.Contain([5, 6]); + collection.Should().Contain([4]); + collection.Should().Contain([5, 6]); }; // Assert @@ -186,6 +187,20 @@ public void When_collection_does_contain_an_expected_item_matching_a_predicate_i "Expected*greater*4*2*"); } + [Fact] + public void Can_chain_another_assertion_on_the_single_result() + { + // Arrange + IEnumerable collection = [1, 2, 3]; + + // Act + Action act = () => collection.Should().Contain(item => item == 2).Which.Should().BeGreaterThan(4); + + // Assert + act.Should().Throw().WithMessage( + "Expected collection[1]*greater*4*2*"); + } + [Fact] public void When_collection_does_contain_an_expected_item_matching_a_predicate_it_should_not_throw() { @@ -395,7 +410,7 @@ public void When_collection_contains_unexpected_items_it_should_throw() } [Fact] - public void When_asserting_multiple_collection_in_assertion_scope_all_should_be_reported() + public void Assertion_scopes_do_not_affect_chained_calls() { // Arrange int[] collection = [1, 2, 3]; @@ -409,7 +424,7 @@ public void When_asserting_multiple_collection_in_assertion_scope_all_should_be_ // Assert act.Should().Throw().WithMessage( - "*to not contain {1, 2}*to not contain 3*"); + "*but found {1, 2}."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainEquivalentOf.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainEquivalentOf.cs index 1a07b2b20a..b8f3028b4b 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainEquivalentOf.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainEquivalentOf.cs @@ -45,6 +45,21 @@ public void When_character_collection_does_contain_equivalent_it_should_succeed( collection.Should().ContainEquivalentOf(item); } + [Fact] + public void Can_chain_a_successive_assertion_on_the_matching_item() + { + // Arrange + char[] collection = "abc123ab".ToCharArray(); + char item = 'c'; + + // Act + var act = () => collection.Should().ContainEquivalentOf(item).Which.Should().Be('C'); + + // Assert + act.Should().Throw() + .WithMessage("Expected collection[2] to be equal to C, but found c."); + } + [Fact] public void When_string_collection_does_contain_same_string_with_other_case_it_should_throw() { @@ -398,17 +413,17 @@ public void When_asserting_collection_to_not_contain_equivalent_it_should_allow_ // Act Action act = () => { - using (new AssertionScope()) - { - collection.Should().NotContainEquivalentOf(another, "because we want to test {0}", "first message") - .And - .HaveCount(4, "because we want to test {0}", "second message"); - } + using var _ = new AssertionScope(); + + collection.Should() + .NotContainEquivalentOf(another, "because we want to test {0}", "first message") + .And + .HaveCount(4, "because we want to test {0}", "second message"); }; // Assert - act.Should().Throw().WithMessage("Expected collection*not to contain*first message*but*.\n" + - "Expected*4 item(s)*because*second message*but*."); + act.Should().Throw() + .WithMessage("Expected collection*not to contain*first message*but found one at index 2.*"); } } } diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainInOrder.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainInOrder.cs index da7dafbc61..0efa455e49 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainInOrder.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainInOrder.cs @@ -90,7 +90,7 @@ public void When_a_collection_does_not_contain_an_ordered_item_it_should_throw_w } [Fact] - public void When_a_collection_does_not_contain_items_with_assertion_scope_all_items_are_reported() + public void Even_with_an_assertion_scope_only_the_first_failure_in_a_chained_assertion_is_reported() { // Act Action act = () => @@ -100,8 +100,7 @@ public void When_a_collection_does_not_contain_items_with_assertion_scope_all_it }; // Assert - act.Should().Throw().WithMessage( - "*but 4 (index 0)*but 5 (index 0)*"); + act.Should().Throw().WithMessage("*but 4 (index 0) did not appear (in the right order)."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainSingle.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainSingle.cs index 260662bc6e..15bf529510 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainSingle.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.ContainSingle.cs @@ -33,11 +33,8 @@ public void When_a_collection_contains_a_single_item_matching_a_predicate_it_sho IEnumerable collection = [1, 2, 3]; Expression> expression = item => item == 2; - // Act - Action act = () => collection.Should().ContainSingle(expression); - - // Assert - act.Should().NotThrow(); + // Act / Assert + collection.Should().ContainSingle(expression); } [Fact] @@ -122,7 +119,27 @@ public void When_single_item_matching_a_predicate_is_found_it_should_allow_conti Action act = () => collection.Should().ContainSingle(item => item == 2).Which.Should().BeGreaterThan(4); // Assert - act.Should().Throw().WithMessage("Expected*greater*4*2*"); + act.Should() + .Throw() + .WithMessage("Expected collection[0]*greater*4*2*"); + } + + [Fact] + public void Chained_assertions_are_never_called_when_the_initial_assertion_failed() + { + // Arrange + IEnumerable collection = [1, 2, 3]; + + // Act + Action act = () => + { + using var _ = new AssertionScope(); + collection.Should().ContainSingle(item => item == 4).Which.Should().BeGreaterThan(4); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expected collection to contain a single item matching (item == 4), but no such item was found."); } [Fact] @@ -239,9 +256,8 @@ public void When_single_item_is_found_it_should_allow_continuation() Action act = () => collection.Should().ContainSingle().Which.Should().BeGreaterThan(4); // Assert - const string expectedMessage = "Expected collection to be greater than 4, but found 3."; - - act.Should().Throw().WithMessage(expectedMessage); + act.Should().Throw() + .WithMessage("Expected collection[0] to be greater than 4, but found 3."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveCount.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveCount.cs index bc0022bbdc..91f58b5fc1 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveCount.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveCount.cs @@ -62,7 +62,7 @@ public void When_collection_has_a_count_larger_than_the_minimum_it_should_not_th } [Fact] - public void When_asserting_a_collection_with_incorrect_predicates_in_assertion_scope_all_are_reported() + public void Even_with_an_assertion_scope_only_the_first_failure_in_a_chained_call_is_reported() { // Arrange int[] collection = [1, 2, 3]; @@ -75,8 +75,7 @@ public void When_asserting_a_collection_with_incorrect_predicates_in_assertion_s }; // Assert - act.Should().Throw().WithMessage( - "*to have a count (c > 3)*to have a count (c < 3)*"); + act.Should().Throw().WithMessage("*count (c > 3), but count is 3: {1, 2, 3}."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveElementAt.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveElementAt.cs index efad04183c..fa9c3c0a6e 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveElementAt.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.HaveElementAt.cs @@ -22,6 +22,20 @@ public void When_collection_has_expected_element_at_specific_index_it_should_not collection.Should().HaveElementAt(1, 2); } + [Fact] + public void Can_chain_another_assertion_on_the_selected_element() + { + // Arrange + int[] collection = [1, 2, 3]; + + // Act + var act = () => collection.Should().HaveElementAt(index: 1, element: 2).Which.Should().Be(3); + + // Assert + act.Should().Throw() + .WithMessage("Expected collection[1] to be 3, but found 2."); + } + [Fact] public void When_collection_does_not_have_the_expected_element_at_specific_index_it_should_throw() { diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.OnlyHaveUniqueItems.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.OnlyHaveUniqueItems.cs index 6c86ba71c4..b07d84886b 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.OnlyHaveUniqueItems.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.OnlyHaveUniqueItems.cs @@ -177,7 +177,7 @@ public void When_a_collection_contains_multiple_duplicate_items_with_a_predicate } [Fact] - public void When_a_collection_contains_multiple_duplicates_on_different_properties_all_should_be_reported() + public void Only_the_first_failing_assertion_in_a_chain_is_reported() { // Arrange IEnumerable collection = @@ -198,7 +198,7 @@ public void When_a_collection_contains_multiple_duplicates_on_different_properti // Assert act.Should().Throw().WithMessage( - "*have unique items on e.Text*have unique items on e.Number*"); + "*have unique items on e.Text*"); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.cs index b908e6669c..ee26a1a148 100644 --- a/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Collections/CollectionAssertionSpecs.cs @@ -12,8 +12,21 @@ namespace FluentAssertions.Specs.Collections; /// public partial class CollectionAssertionSpecs { - public class Chainings + public class Chaining { + [Fact] + public void Chaining_something_should_do_something() + { + // Act + var languages = new[] { "C#" }; + + var act = () => languages.Should().ContainSingle() + .Which.Should().EndWith("script"); + + // Assert + act.Should().Throw().WithMessage("Expected languages[0]*"); + } + [Fact] public void Should_support_chaining_constraints_with_and() { @@ -71,7 +84,7 @@ public void When_the_collection_is_not_ordered_according_to_the_subsequent_ascen // Assert action.Should().Throw() - .WithMessage("Expected collection * to be ordered \"by Item2\"*"); + .WithMessage("Expected collection*to be ordered \"by Item2\"*"); } [Fact] @@ -163,7 +176,7 @@ public void When_the_collection_is_not_ordered_according_to_the_subsequent_desce // Assert action.Should().Throw() - .WithMessage("Expected collection * to be ordered \"by Item2\"*"); + .WithMessage("Expected collection*to be ordered \"by Item2\"*"); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Collections/GenericCollectionAssertionOfStringSpecs.ContainMatch.cs b/Tests/FluentAssertions.Specs/Collections/GenericCollectionAssertionOfStringSpecs.ContainMatch.cs index d274fe904f..6901a1e685 100644 --- a/Tests/FluentAssertions.Specs/Collections/GenericCollectionAssertionOfStringSpecs.ContainMatch.cs +++ b/Tests/FluentAssertions.Specs/Collections/GenericCollectionAssertionOfStringSpecs.ContainMatch.cs @@ -37,7 +37,21 @@ public void When_collection_contains_multiple_matches_it_should_not_throw() } [Fact] - public void When_collection_contains_multiple_matches_which_should_throw() + public void Can_chain_another_assertion_if_a_single_string_matches_the_pattern() + { + // Arrange + IEnumerable collection = ["build succeeded", "test succeeded", "pack failed"]; + + // Act + Action action = () => collection.Should().ContainMatch("*failed*").Which.Should().StartWith("test"); + + // Assert + action.Should().Throw() + .WithMessage("Expected collection[2] to start with*test*pack failed*"); + } + + [Fact] + public void Cannot_chain_another_assertion_if_multiple_strings_match_the_pattern() { // Arrange IEnumerable collection = ["build succeded", "test failed", "pack failed"]; diff --git a/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValue.cs b/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValue.cs index 639b329436..3cfd9ab9d9 100644 --- a/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValue.cs +++ b/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValue.cs @@ -27,6 +27,23 @@ public void When_dictionary_contains_expected_value_it_should_succeed() act.Should().NotThrow(); } + [Fact] + public void Can_continue_asserting_on_a_single_matching_item() + { + // Arrange + var dictionary = new Dictionary + { + [1] = "One", + [2] = "Two" + }; + + // Act + Action act = () => dictionary.Should().ContainValue("One").Which.Should().Be("Two"); + + // Assert + act.Should().Throw().WithMessage("*Expected dictionary[1] to be*Two*, but*One*differs*"); + } + [Fact] public void Null_dictionaries_do_not_contain_any_values() { @@ -82,30 +99,6 @@ public void When_the_specified_value_exists_it_should_allow_continuation_using_t act.Should().Throw().WithMessage("Expected*greater*0*0*"); } - [Fact] - public void When_multiple_matches_for_the_specified_value_exist_continuation_using_the_matched_value_should_fail() - { - // Arrange - var myClass = new MyClass { SomeProperty = 0 }; - - var dictionary = new Dictionary - { - [1] = myClass, - [2] = new() { SomeProperty = 0 } - }; - - // Act - Action act = - () => - dictionary.Should() - .ContainValue(new MyClass { SomeProperty = 0 }) - .Which.Should() - .BeSameAs(myClass); - - // Assert - act.Should().Throw(); - } - [Fact] public void When_a_dictionary_does_not_contain_single_value_it_should_throw_with_clear_explanation() { diff --git a/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValues.cs b/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValues.cs index ec0247fb4d..07f898177b 100644 --- a/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValues.cs +++ b/Tests/FluentAssertions.Specs/Collections/GenericDictionaryAssertionSpecs.ContainValues.cs @@ -42,7 +42,7 @@ public void When_a_dictionary_does_not_contain_a_number_of_values_it_should_thro // Assert act.Should().Throw().WithMessage( - "Expected dictionary {[1] = \"One\", [2] = \"Two\"} to contain value {\"Two\", \"Three\"} because we do, but could not find {\"Three\"}."); + "Expected dictionary {[1] = \"One\", [2] = \"Two\"} to contain values {\"Two\", \"Three\"} because we do, but could not find \"Three\"."); } [Fact] @@ -65,6 +65,23 @@ public void } } + [Fact] + public void Can_run_another_assertion_on_the_result() + { + // Arrange + var dictionary = new Dictionary + { + [1] = "One", + [2] = "Two" + }; + + // Act + Action act = () => dictionary.Should().ContainValues("Two", "One").Which.Should().Contain("Three"); + + // Assert + act.Should().Throw().WithMessage("Expected dictionary[1 and 2]*to contain*Three*"); + } + public class NotContainValues { [Fact] diff --git a/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs index 9933c52a5c..69d8e077c3 100644 --- a/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs @@ -435,29 +435,6 @@ public void .WithMessage("*no*exception*that's what he told me*but*ArgumentNullException*"); } - [Fact] - public void When_an_assertion_fails_on_NotThrow_succeeding_message_should_be_included() - { - // Arrange - Func throwingFunction = () => throw new Exception(); - - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - throwingFunction.Should().NotThrow() - .And.BeNull(); - }; - - // Assert - act.Should().Throw() - .WithMessage( - "*Did not expect any exception*" + - "*to be *" - ); - } - #endregion #region NotThrowAfter @@ -594,10 +571,10 @@ public void When_no_exception_should_be_thrown_after_wait_time_the_func_result_s // Act Action act = () => throwShorterThanWaitTime.Should(clock).NotThrowAfter(waitTime, pollInterval) - .Which.Should().Be(42); + .Which.Should().Be(43); // Assert - act.Should().NotThrow(); + act.Should().Throw().WithMessage("Expected throwShorterThanWaitTime.Result to be 43*"); } [Fact] @@ -619,10 +596,7 @@ public void When_an_assertion_fails_on_NotThrowAfter_succeeding_message_should_b // Assert act.Should().Throw() - .WithMessage( - "*Did not expect any exceptions after*" + - "*to be *" - ); + .WithMessage("*Did not expect any exceptions after*"); } #endregion diff --git a/Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.Chaining.cs b/Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.Chaining.cs new file mode 100644 index 0000000000..11e959587d --- /dev/null +++ b/Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.Chaining.cs @@ -0,0 +1,599 @@ +using System; +using FluentAssertions.Execution; +using Xunit; +using Xunit.Sdk; + +namespace FluentAssertions.Specs.Execution; + +/// +/// The chaining API specs. +/// +public partial class AssertionChainSpecs +{ + public class Chaining + { + [Fact] + public void A_successful_assertion_does_not_affect_the_chained_failing_assertion() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(condition: true) + .FailWith("First assertion") + .Then + .FailWith("Second assertion"); + + // Arrange + act.Should().Throw().WithMessage("*Second assertion*"); + } + + [Fact] + public void When_the_previous_assertion_succeeded_it_should_not_affect_the_next_one_with_arguments() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .FailWith("Second {0}", "assertion"); + + // Assert + act.Should().Throw() + .WithMessage("Second \"assertion\""); + } + + [Fact] + public void When_the_previous_assertion_succeeded_it_should_not_affect_the_next_one_with_argument_providers() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .FailWith("Second {0}", () => "assertion"); + + // Assert + act.Should().Throw() + .WithMessage("Second \"assertion\""); + } + + [Fact] + public void When_the_previous_assertion_succeeded_it_should_not_affect_the_next_one_with_a_fail_reason_function() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .FailWith(() => new FailReason("Second {0}", "assertion")); + + // Assert + act.Should().Throw() + .WithMessage("Second \"assertion\""); + } + + [Fact] + public void When_continuing_an_assertion_chain_the_reason_should_be_part_of_consecutive_failures() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .BecauseOf("because reasons") + .FailWith("Expected{reason}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected because reasons"); + } + + [Fact] + public void When_continuing_an_assertion_chain_the_reason_with_arguments_should_be_part_of_consecutive_failures() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .BecauseOf("because {0}", "reasons") + .FailWith("Expected{reason}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected because reasons"); + } + + [Fact] + public void Passing_a_null_value_as_reason_does_not_fail() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .BecauseOf(null, "only because for method disambiguity") + .ForCondition(false) + .FailWith("First assertion"); + + // Assert + act.Should().Throw() + .WithMessage("First assertion"); + } + + [Fact] + public void When_a_given_is_used_before_an_assertion_then_the_result_should_be_available_for_evaluation() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .Given(() => new[] { "a", "b" }) + .ForCondition(collection => collection.Length > 0) + .FailWith("First assertion"); + + // Assert + act.Should().NotThrow(); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_evaluate_the_succeeding_given_statement() + { + // Arrange + using var _ = new AssertionScope(new IgnoringFailuresAssertionStrategy()); + + // Act / Assert + AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("First assertion") + .Then + .Given(() => throw new InvalidOperationException()); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_evaluate_the_succeeding_condition() + { + // Arrange + bool secondConditionEvaluated = false; + + try + { + using var _ = new AssertionScope(); + + // Act + AssertionChain.GetOrCreate() + .Given(() => (string)null) + .ForCondition(s => s is not null) + .FailWith("but is was null") + .Then + .ForCondition(_ => secondConditionEvaluated = true) + .FailWith("it should be 42"); + } + catch + { + // Ignore + } + + // Assert + secondConditionEvaluated.Should().BeFalse("because the 2nd condition should not be invoked"); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure() + { + // Arrange + var scope = new AssertionScope(); + + // Act + AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("First assertion") + .Then + .ForCondition(false) + .FailWith("Second assertion"); + + string[] failures = scope.Discard(); + scope.Dispose(); + + Assert.Single(failures); + Assert.Contains("First assertion", failures); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure_with_arguments() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + + AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("First assertion") + .Then + .FailWith("Second {0}", "assertion"); + }; + + // Assert + act.Should().Throw() + .WithMessage("First assertion"); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure_with_argument_providers() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + + AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("First assertion") + .Then + .FailWith("Second {0}", () => "assertion"); + }; + + // Assert + act.Should().Throw() + .WithMessage("First assertion"); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure_with_a_fail_reason_function() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + + AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("First assertion") + .Then + .FailWith(() => new FailReason("Second {0}", "assertion")); + }; + + // Assert + act.Should().Throw() + .WithMessage("First assertion"); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_expectation() + { + // Act + Action act = () => + { + using var scope = new AssertionScope(); + + AssertionChain.GetOrCreate() + .WithExpectation("Expectations are the root ", c => c + .ForCondition(false) + .FailWith("of disappointment") + .Then + .WithExpectation("Assumptions are the root ", c2 => c2 + .FailWith("of all evil"))); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expectations are the root of disappointment"); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_expectation_with_arguments() + { + // Act + Action act = () => + { + using var scope = new AssertionScope(); + + AssertionChain.GetOrCreate() + .WithExpectation("Expectations are the {0} ", "root", c => c + .ForCondition(false) + .FailWith("of disappointment") + .Then + .WithExpectation("Assumptions are the {0} ", "root", c2 => c2 + .FailWith("of all evil"))); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expectations are the \"root\" of disappointment"); + } + + [Fact] + public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_default_identifier() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + + AssertionChain.GetOrCreate() + .WithDefaultIdentifier("identifier") + .ForCondition(false) + .FailWith("Expected {context}") + .Then + .WithDefaultIdentifier("other") + .FailWith("Expected {context}"); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expected identifier"); + } + + [Fact] + public void When_continuing_a_failed_assertion_chain_consecutive_reasons_are_ignored() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + + AssertionChain.GetOrCreate() + .BecauseOf("because {0}", "whatever") + .ForCondition(false) + .FailWith("Expected{reason}") + .Then + .BecauseOf("because reasons") + .FailWith("Expected{reason}"); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expected because whatever"); + } + + [Fact] + public void When_continuing_a_failed_assertion_chain_consecutive_reasons_with_arguments_are_ignored() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + + AssertionChain.GetOrCreate() + .BecauseOf("because {0}", "whatever") + .ForCondition(false) + .FailWith("Expected{reason}") + .Then + .BecauseOf("because {0}", "reasons") + .FailWith("Expected{reason}"); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expected because whatever"); + } + + [Fact] + public void When_the_previous_assertion_succeeded_it_should_evaluate_the_succeeding_given_statement() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .Given(() => throw new InvalidOperationException()); + + // Assert + act.Should().Throw(); + } + + [Fact] + public void When_the_previous_assertion_succeeded_it_should_not_affect_the_succeeding_expectation() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .WithExpectation("Expectations are the root ", chain => chain + .ForCondition(true) + .FailWith("of disappointment") + .Then + .WithExpectation("Assumptions are the root ", innerChain => innerChain + .FailWith("of all evil"))); + }; + + // Assert + act.Should().Throw() + .WithMessage("Assumptions are the root of all evil"); + } + + [Fact] + public void When_the_previous_assertion_succeeded_it_should_not_affect_the_succeeding_expectation_with_arguments() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .WithExpectation("Expectations are the {0} ", "root", c => c + .ForCondition(true) + .FailWith("of disappointment") + .Then + .WithExpectation("Assumptions are the {0} ", "root", c2 => c2 + .FailWith("of all evil"))); + }; + + // Assert + act.Should().Throw() + .WithMessage("Assumptions are the \"root\" of all evil"); + } + + [Fact] + public void When_the_previous_assertion_succeeded_it_should_not_affect_the_succeeding_default_identifier() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .WithDefaultIdentifier("identifier") + .ForCondition(true) + .FailWith("Expected {context}") + .Then + .WithDefaultIdentifier("other") + .FailWith("Expected {context}"); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expected other"); + } + + [Fact] + public void Continuing_an_assertion_with_occurrence() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .WithExpectation("{expectedOccurrence} ", c => c + .ForConstraint(Exactly.Once(), 2) + .FailWith("Second {0}", "assertion")); + }; + + // Assert + act.Should().Throw() + .WithMessage("Exactly 1 time Second \"assertion\"*"); + } + + [Fact] + public void Continuing_an_assertion_with_occurrence_will_not_be_executed_when_first_assertion_fails() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("First assertion") + .Then + .WithExpectation("{expectedOccurrence} ", c => c + .ForConstraint(Exactly.Once(), 2) + .FailWith("Second {0}", "assertion")); + }; + + // Assert + act.Should().Throw() + .WithMessage("First assertion"); + } + + [Fact] + public void Continuing_an_assertion_with_occurrence_overrides_the_previous_defined_expectations() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .WithExpectation("First expectation", c => c + .ForCondition(true) + .FailWith("First assertion") + .Then + .WithExpectation("{expectedOccurrence} ", c2 => c2 + .ForConstraint(Exactly.Once(), 2) + .FailWith("Second {0}", "assertion"))); + }; + + // Assert + act.Should().Throw() + .WithMessage("Exactly 1 time Second \"assertion\"*"); + } + + [Fact] + public void Continuing_an_assertion_after_occurrence_check_works() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .WithExpectation("{expectedOccurrence} ", c => c + .ForConstraint(Exactly.Once(), 1) + .FailWith("First assertion") + .Then + .WithExpectation("Second expectation ", c2 => c2 + .ForCondition(false) + .FailWith("Second {0}", "assertion"))); + }; + + // Assert + act.Should().Throw() + .WithMessage("Second expectation Second \"assertion\"*"); + } + + [Fact] + public void Continuing_an_assertion_with_occurrence_check_before_defining_expectation_works() + { + // Act + Action act = () => + { + AssertionChain.GetOrCreate() + .ForCondition(true) + .FailWith("First assertion") + .Then + .ForConstraint(Exactly.Once(), 2) + .WithExpectation("Second expectation ", c => c + .FailWith("Second {0}", "assertion")); + }; + + // Assert + act.Should().Throw() + .WithMessage("Second expectation Second \"assertion\"*"); + } + + [Fact] + public void Does_not_continue_a_chained_assertion_after_the_first_one_failed_the_occurrence_check() + { + // Arrange + var scope = new AssertionScope(); + + // Act + AssertionChain.GetOrCreate() + .ForConstraint(Exactly.Once(), 2) + .FailWith("First {0}", "assertion") + .Then + .ForConstraint(Exactly.Once(), 2) + .FailWith("Second {0}", "assertion"); + + string[] failures = scope.Discard(); + + // Assert + Assert.Single(failures); + Assert.Contains("First \"assertion\"", failures); + } + + [Fact] + public void Discard_a_scope_after_continuing_chained_assertion() + { + // Arrange + using var scope = new AssertionScope(); + + // Act + AssertionChain.GetOrCreate() + .ForConstraint(Exactly.Once(), 2) + .FailWith("First {0}", "assertion"); + + var failures = scope.Discard(); + + // Assert + Assert.Single(failures); + Assert.Contains("First \"assertion\"", failures); + } + + // [Fact] + // public void Get_info_about_line_breaks_from_parent_scope_after_continuing_chained_assertion() + // { + // // Arrange + // using var scope = new AssertionScope(); + // scope.FormattingOptions.UseLineBreaks = true; + // + // // Act + // var innerScope = AssertionChain.GetOrCreate() + // .ForConstraint(Exactly.Once(), 1) + // .FailWith("First {0}", "assertion") + // .Then + // .UsingLineBreaks; + // + // // Assert + // innerScope.UsingLineBreaks.Should().Be(scope.UsingLineBreaks); + // } + } +} diff --git a/Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.MessageFormating.cs b/Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.MessageFormating.cs new file mode 100644 index 0000000000..63fc8e684e --- /dev/null +++ b/Tests/FluentAssertions.Specs/Execution/AssertionChainSpecs.MessageFormating.cs @@ -0,0 +1,390 @@ +using System; +using System.Collections.Generic; +using FluentAssertions.Execution; +using Xunit; +using Xunit.Sdk; + +namespace FluentAssertions.Specs.Execution; + +/// +/// The message formatting specs. +/// +public partial class AssertionChainSpecs +{ + public class MessageFormatting + { + [Fact] + public void Multiple_assertions_in_an_assertion_scope_are_all_reported() + { + // Arrange + var scope = new AssertionScope(); + + AssertionChain.GetOrCreate().FailWith("Failure"); + AssertionChain.GetOrCreate().FailWith("Failure"); + + using (new AssertionScope()) + { + AssertionChain.GetOrCreate().FailWith("Failure"); + AssertionChain.GetOrCreate().FailWith("Failure"); + } + + // Act + Action act = scope.Dispose; + + // Assert + act.Should().Throw() + .Which.Message.Should().Contain("Failure", Exactly.Times(4)); + } + + [InlineData("foo")] + [InlineData("{}")] + [Theory] + public void The_failure_message_uses_the_name_of_the_scope_as_context(string context) + { + // Act + Action act = () => + { + using var _ = new AssertionScope(context); + new[] { 1, 2, 3 }.Should().Equal(3, 2, 1); + }; + + // Assert + act.Should().Throw() + .WithMessage($"Expected {context} to be equal to*"); + } + + [Fact] + public void The_failure_message_uses_the_lazy_name_of_the_scope_as_context() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(() => "lazy foo"); + new[] { 1, 2, 3 }.Should().Equal(3, 2, 1); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expected lazy foo to be equal to*"); + } + + [Fact] + public void The_failure_message_includes_all_failures() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + var values = new Dictionary(); + values.Should().ContainKey(0); + values.Should().ContainKey(1); + }; + + // Assert + act.Should().Throw() + .WithMessage( + "Expected * to contain key 0.\nExpected * to contain key 1.\n"); + } + + [Fact] + public void The_failure_message_includes_all_failures_as_well() + { + // Act + Action act = () => + { + using var _ = new AssertionScope(); + var values = new List(); + values.Should().ContainSingle(); + values.Should().ContainSingle(); + }; + + // Assert + act.Should().Throw() + .WithMessage( + "Expected * to contain a single item, but the collection is empty.\n" + + "Expected * to contain a single item, but the collection is empty.\n"); + } + + [Fact] + public void The_reason_can_contain_parentheses() + { + // Act + Action act = () => 1.Should().Be(2, "can't use these in becauseArgs: {0} {1}", "{", "}"); + + // Assert + act.Should().Throw() + .WithMessage("*because can't use these in becauseArgs: { }*"); + } + + [Fact] + public void Because_reason_should_ignore_undefined_arguments() + { + // Act + object[] becauseArgs = null; + Action act = () => 1.Should().Be(2, "it should still work", becauseArgs); + + // Assert + act.Should().Throw() + .WithMessage("*because it should still work*"); + } + + [Fact] + public void Because_reason_should_threat_parentheses_as_literals_if_no_arguments_are_defined() + { + // Act +#pragma warning disable CA2241 + // ReSharper disable once FormatStringProblem + Action act = () => 1.Should().Be(2, "use of {} is okay if there are no because arguments"); +#pragma warning restore CA2241 + + // Assert + act.Should().Throw() + .WithMessage("*because use of {} is okay if there are no because arguments*"); + } + + [Fact] + public void Because_reason_should_inform_about_invalid_parentheses_with_a_default_message() + { + // Act +#pragma warning disable CA2241 + // ReSharper disable once FormatStringProblem + Action act = () => 1.Should().Be(2, "use of {} is considered invalid in because parameter with becauseArgs", + "additional becauseArgs argument"); +#pragma warning restore CA2241 + + // Assert + act.Should().Throw() + .WithMessage( + "*because message 'use of {} is considered invalid in because parameter with becauseArgs' could not be formatted with string.Format*"); + } + + [Fact] + public void Message_should_keep_parentheses_in_literal_values() + { + // Act + Action act = () => "{foo}".Should().Be("{bar}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected string to be \"{bar}\", but \"{foo}\" differs near*"); + } + + [Fact] + public void Message_should_contain_literal_value_if_marked_with_double_parentheses() + { + // Act + Action act = () => AssertionChain.GetOrCreate().FailWith("{{empty}}"); + + // Assert + act.Should().ThrowExactly() + .WithMessage("{empty}*"); + } + + [InlineData("\r")] + [InlineData("\\r")] + [InlineData("\\\r")] + [InlineData("\\\\r")] + [InlineData("\\\\\r")] + [InlineData("\n")] + [InlineData("\\n")] + [InlineData("\\\n")] + [InlineData("\\\\n")] + [InlineData("\\\\\n")] + [Theory] + public void Message_should_not_have_modified_carriage_return_or_line_feed_control_characters(string str) + { + // Act + Action act = () => AssertionChain.GetOrCreate().FailWith(str); + + // Assert + act.Should().ThrowExactly() + .WithMessage(str); + } + + [InlineData("\r")] + [InlineData("\\r")] + [InlineData("\\\r")] + [InlineData(@"\\r")] + [InlineData("\\\\\r")] + [InlineData("\n")] + [InlineData("\\n")] + [InlineData("\\\n")] + [InlineData(@"\\n")] + [InlineData("\\\\\n")] + [Theory] + public void Message_should_not_have_modified_carriage_return_or_line_feed_control_characters_in_supplied_arguments( + string str) + { + // Act + Action act = () => AssertionChain.GetOrCreate().FailWith(@"\{0}\A", str); + + // Assert + act.Should().ThrowExactly() + .WithMessage("\\\"" + str + "\"\\A*"); + } + + [Fact] + public void Message_should_not_have_trailing_backslashes_removed_from_subject() + { + // Arrange / Act + Action act = () => "A\\".Should().Be("A"); + + // Assert + act.Should().Throw() + .WithMessage("""* near "\" *"""); + } + + [Fact] + public void Message_should_not_have_trailing_backslashes_removed_from_expectation() + { + // Arrange / Act + Action act = () => "A".Should().Be("A\\"); + + // Assert + act.Should().Throw() + .WithMessage("""* to be "A\" *"""); + } + + [Fact] + public void Message_should_have_reportable_values_appended_at_the_end() + { + // Arrange + var scope = new AssertionScope(); + scope.AddReportable("SomeKey", "SomeValue"); + scope.AddReportable("AnotherKey", "AnotherValue"); + + AssertionChain.GetOrCreate().FailWith("{SomeKey}{AnotherKey}"); + + // Act + Action act = scope.Dispose; + + // Assert + act.Should().ThrowExactly() + .WithMessage("*With SomeKey:\nSomeValue\nWith AnotherKey:\nAnotherValue"); + } + + [Fact] + public void Deferred_reportable_values_should_not_be_calculated_in_absence_of_failures() + { + // Arrange + var scope = new AssertionScope(); + var deferredValueInvoked = false; + + scope.AddReportable("MyKey", () => + { + deferredValueInvoked = true; + + return "MyValue"; + }); + + // Act + scope.Dispose(); + + // Assert + deferredValueInvoked.Should().BeFalse(); + } + + [Fact] + public void Message_should_start_with_the_defined_expectation() + { + // Act + Action act = () => + { + var assertion = AssertionChain.GetOrCreate(); + + assertion + .WithExpectation("Expectations are the root ", chain => chain + .ForCondition(false) + .FailWith("of disappointment")); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expectations are the root of disappointment"); + } + + [Fact] + public void Message_should_start_with_the_defined_expectation_and_arguments() + { + // Act + Action act = () => + { + var assertion = AssertionChain.GetOrCreate(); + + assertion + .WithExpectation("Expectations are the {0} ", "root", chain => chain.ForCondition(false) + .FailWith("of disappointment")); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expectations are the \"root\" of disappointment"); + } + + [Fact] + public void Message_should_contain_object_as_context_if_identifier_can_not_be_resolved() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("Expected {context}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected object"); + } + + [Fact] + public void Message_should_contain_the_fallback_value_as_context_if_identifier_can_not_be_resolved() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .ForCondition(false) + .FailWith("Expected {context:fallback}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected fallback"); + } + + [Fact] + public void Message_should_contain_the_default_identifier_as_context_if_identifier_can_not_be_resolved() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .WithDefaultIdentifier("identifier") + .ForCondition(false) + .FailWith("Expected {context}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected identifier"); + } + + [Fact] + public void Message_should_contain_the_reason_as_defined() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .BecauseOf("because reasons") + .FailWith("Expected{reason}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected because reasons"); + } + + [Fact] + public void Message_should_contain_the_reason_as_defined_with_arguments() + { + // Act + Action act = () => AssertionChain.GetOrCreate() + .BecauseOf("because {0}", "reasons") + .FailWith("Expected{reason}"); + + // Assert + act.Should().Throw() + .WithMessage("Expected because reasons"); + } + } +} diff --git a/Tests/FluentAssertions.Specs/Execution/AssertionScope.ChainingApiSpecs.cs b/Tests/FluentAssertions.Specs/Execution/AssertionScope.ChainingApiSpecs.cs deleted file mode 100644 index 11f21eae89..0000000000 --- a/Tests/FluentAssertions.Specs/Execution/AssertionScope.ChainingApiSpecs.cs +++ /dev/null @@ -1,588 +0,0 @@ -using System; -using FluentAssertions.Execution; -using Xunit; -using Xunit.Sdk; - -namespace FluentAssertions.Specs.Execution; - -/// -/// The chaining API specs. -/// -public partial class AssertionScopeSpecs -{ - [Fact] - public void When_the_previous_assertion_succeeded_it_should_not_affect_the_next_one() - { - bool succeeded = false; - - // Act - try - { - Execute.Assertion - .ForCondition(condition: true) - .FailWith("First assertion") - .Then - .FailWith("Second assertion"); - } - catch (Exception e) - { - // Assert - succeeded = e is XunitException xUnitException && xUnitException.Message.Contains("Second"); - } - - if (!succeeded) - { - throw new XunitException("Expected the second assertion to fail"); - } - } - - [Fact] - public void When_the_previous_assertion_succeeded_it_should_not_affect_the_next_one_with_arguments() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .FailWith("Second {0}", "assertion"); - - // Assert - act.Should().Throw() - .WithMessage("Second \"assertion\""); - } - - [Fact] - public void When_the_previous_assertion_succeeded_it_should_not_affect_the_next_one_with_argument_providers() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .FailWith("Second {0}", () => "assertion"); - - // Assert - act.Should().Throw() - .WithMessage("Second \"assertion\""); - } - - [Fact] - public void When_the_previous_assertion_succeeded_it_should_not_affect_the_next_one_with_a_fail_reason_function() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .FailWith(() => new FailReason("Second {0}", "assertion")); - - // Assert - act.Should().Throw() - .WithMessage("Second \"assertion\""); - } - - [Fact] - public void When_continuing_an_assertion_chain_the_reason_should_be_part_of_consecutive_failures() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .BecauseOf("because reasons") - .FailWith("Expected{reason}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected because reasons"); - } - - [Fact] - public void When_continuing_an_assertion_chain_the_reason_with_arguments_should_be_part_of_consecutive_failures() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .BecauseOf("because {0}", "reasons") - .FailWith("Expected{reason}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected because reasons"); - } - - [Fact] - public void Passing_a_null_value_as_reason_does_not_fail() - { - // Act - Action act = () => Execute.Assertion - .BecauseOf(null, "only because for method disambiguity") - .ForCondition(false) - .FailWith("First assertion"); - - // Assert - act.Should().Throw() - .WithMessage("First assertion"); - } - - [Fact] - public void When_a_given_is_used_before_an_assertion_then_the_result_should_be_available_for_evaluation() - { - // Act - Action act = () => Execute.Assertion - .Given(() => new[] { "a", "b" }) - .ForCondition(collection => collection.Length > 0) - .FailWith("First assertion"); - - // Assert - act.Should().NotThrow(); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_evaluate_the_succeeding_given_statement() - { - // Arrange - using var _ = new AssertionScope(new IgnoringFailuresAssertionStrategy()); - - // Act / Assert - Execute.Assertion - .ForCondition(false) - .FailWith("First assertion") - .Then - .Given(() => throw new InvalidOperationException()); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_evaluate_the_succeeding_condition() - { - // Arrange - bool secondConditionEvaluated = false; - - try - { - using var _ = new AssertionScope(); - - // Act - Execute.Assertion - .Given(() => (string)null) - .ForCondition(s => s is not null) - .FailWith("but is was null") - .Then - .ForCondition(_ => secondConditionEvaluated = true) - .FailWith("it should be 42"); - } - catch - { - // Ignore - } - - // Assert - secondConditionEvaluated.Should().BeFalse("because the 2nd condition should not be invoked"); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure() - { - // Arrange - var scope = new AssertionScope(); - - // Act - Execute.Assertion - .ForCondition(false) - .FailWith("First assertion") - .Then - .ForCondition(false) - .FailWith("Second assertion"); - - string[] failures = scope.Discard(); - scope.Dispose(); - - Assert.Single(failures); - Assert.Contains("First assertion", failures); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure_with_arguments() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .ForCondition(false) - .FailWith("First assertion") - .Then - .FailWith("Second {0}", "assertion"); - }; - - // Assert - act.Should().Throw() - .WithMessage("First assertion"); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure_with_argument_providers() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .ForCondition(false) - .FailWith("First assertion") - .Then - .FailWith("Second {0}", () => "assertion"); - }; - - // Assert - act.Should().Throw() - .WithMessage("First assertion"); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_failure_with_a_fail_reason_function() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .ForCondition(false) - .FailWith("First assertion") - .Then - .FailWith(() => new FailReason("Second {0}", "assertion")); - }; - - // Assert - act.Should().Throw() - .WithMessage("First assertion"); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_expectation() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .WithExpectation("Expectations are the root ") - .ForCondition(false) - .FailWith("of disappointment") - .Then - .WithExpectation("Assumptions are the root ") - .FailWith("of all evil"); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expectations are the root of disappointment"); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_expectation_with_arguments() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .WithExpectation("Expectations are the {0} ", "root") - .ForCondition(false) - .FailWith("of disappointment") - .Then - .WithExpectation("Assumptions are the {0} ", "root") - .FailWith("of all evil"); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expectations are the \"root\" of disappointment"); - } - - [Fact] - public void When_the_previous_assertion_failed_it_should_not_execute_the_succeeding_default_identifier() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .WithDefaultIdentifier("identifier") - .ForCondition(false) - .FailWith("Expected {context}") - .Then - .WithDefaultIdentifier("other") - .FailWith("Expected {context}"); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expected identifier"); - } - - [Fact] - public void When_continuing_a_failed_assertion_chain_consecutive_resons_are_ignored() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .BecauseOf("because {0}", "whatever") - .ForCondition(false) - .FailWith("Expected{reason}") - .Then - .BecauseOf("because reasons") - .FailWith("Expected{reason}"); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expected because whatever"); - } - - [Fact] - public void When_continuing_a_failed_assertion_chain_consecutive_resons_with_arguments_are_ignored() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - - Execute.Assertion - .BecauseOf("because {0}", "whatever") - .ForCondition(false) - .FailWith("Expected{reason}") - .Then - .BecauseOf("because {0}", "reasons") - .FailWith("Expected{reason}"); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expected because whatever"); - } - - [Fact] - public void When_the_previous_assertion_succeeded_it_should_evaluate_the_succeeding_given_statement() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .Given(() => throw new InvalidOperationException()); - - // Assert - Assert.Throws(act); - } - - [Fact] - public void When_the_previous_assertion_succeeded_it_should_not_affect_the_succeeding_expectation() - { - // Act - Action act = () => Execute.Assertion - .WithExpectation("Expectations are the root ") - .ForCondition(true) - .FailWith("of disappointment") - .Then - .WithExpectation("Assumptions are the root ") - .FailWith("of all evil"); - - // Assert - act.Should().Throw() - .WithMessage("Assumptions are the root of all evil"); - } - - [Fact] - public void When_the_previous_assertion_succeeded_it_should_not_affect_the_succeeding_expectation_with_arguments() - { - // Act - Action act = () => Execute.Assertion - .WithExpectation("Expectations are the {0} ", "root") - .ForCondition(true) - .FailWith("of disappointment") - .Then - .WithExpectation("Assumptions are the {0} ", "root") - .FailWith("of all evil"); - - // Assert - act.Should().Throw() - .WithMessage("Assumptions are the \"root\" of all evil"); - } - - [Fact] - public void When_the_previous_assertion_succeeded_it_should_not_affect_the_succeeding_default_identifier() - { - // Act - Action act = () => - { - Execute.Assertion - .WithDefaultIdentifier("identifier") - .ForCondition(true) - .FailWith("Expected {context}") - .Then - .WithDefaultIdentifier("other") - .FailWith("Expected {context}"); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expected other"); - } - - [Fact] - public void Continuing_an_assertion_with_occurrence() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .WithExpectation("{expectedOccurrence} ") - .ForConstraint(Exactly.Once(), 2) - .FailWith("Second {0}", "assertion"); - - // Assert - act.Should().Throw() - .WithMessage("Exactly 1 time Second \"assertion\"*"); - } - - [Fact] - public void Continuing_an_assertion_with_occurrence_will_not_be_executed_when_first_assertion_fails() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(false) - .FailWith("First assertion") - .Then - .WithExpectation("{expectedOccurrence} ") - .ForConstraint(Exactly.Once(), 2) - .FailWith("Second {0}", "assertion"); - - // Assert - act.Should().Throw() - .WithMessage("First assertion"); - } - - [Fact] - public void Continuing_an_assertion_with_occurrence_overrides_the_previous_defined_expectations() - { - // Act - Action act = () => Execute.Assertion - .WithExpectation("First expectation") - .ForCondition(true) - .FailWith("First assertion") - .Then - .WithExpectation("{expectedOccurrence} ") - .ForConstraint(Exactly.Once(), 2) - .FailWith("Second {0}", "assertion"); - - // Assert - act.Should().Throw() - .WithMessage("Exactly 1 time Second \"assertion\"*"); - } - - [Fact] - public void Continuing_an_assertion_after_occurrence_check_works() - { - // Act - Action act = () => Execute.Assertion - .WithExpectation("{expectedOccurrence} ") - .ForConstraint(Exactly.Once(), 1) - .FailWith("First assertion") - .Then - .WithExpectation("Second expectation ") - .ForCondition(false) - .FailWith("Second {0}", "assertion"); - - // Assert - act.Should().Throw() - .WithMessage("Second expectation Second \"assertion\"*"); - } - - [Fact] - public void Continuing_an_assertion_with_occurrence_check_before_defining_expectation_works() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(true) - .FailWith("First assertion") - .Then - .ForConstraint(Exactly.Once(), 2) - .WithExpectation("Second expectation ") - .FailWith("Second {0}", "assertion"); - - // Assert - act.Should().Throw() - .WithMessage("Second expectation Second \"assertion\"*"); - } - - [Fact] - public void Does_not_continue_a_chained_assertion_after_the_first_one_failed_the_occurrence_check() - { - // Arrange - var scope = new AssertionScope(); - - // Act - Execute.Assertion - .ForConstraint(Exactly.Once(), 2) - .FailWith("First {0}", "assertion") - .Then - .ForConstraint(Exactly.Once(), 2) - .FailWith("Second {0}", "assertion"); - - string[] failures = scope.Discard(); - scope.Dispose(); - - // Assert - Assert.Single(failures); - Assert.Contains("First \"assertion\"", failures); - } - - [Fact] - public void Discard_a_scope_after_continuing_chained_assertion() - { - // Arrange - using var scope = new AssertionScope(); - - // Act - var failures = Execute.Assertion - .ForConstraint(Exactly.Once(), 2) - .FailWith("First {0}", "assertion") - .Then - .Discard(); - - // Assert - Assert.Single(failures); - Assert.Contains("First \"assertion\"", failures); - } - - [Fact] - public void Get_info_about_line_breaks_from_parent_scope_after_continuing_chained_assertion() - { - // Arrange - using var scope = new AssertionScope(); - scope.FormattingOptions.UseLineBreaks = true; - - // Act - var innerScope = Execute.Assertion - .ForConstraint(Exactly.Once(), 1) - .FailWith("First {0}", "assertion") - .Then - .UsingLineBreaks; - - // Assert - innerScope.UsingLineBreaks.Should().Be(scope.UsingLineBreaks); - } -} diff --git a/Tests/FluentAssertions.Specs/Execution/AssertionScope.ContextDataSpecs.cs b/Tests/FluentAssertions.Specs/Execution/AssertionScope.ContextDataSpecs.cs deleted file mode 100644 index 1a36fb62bc..0000000000 --- a/Tests/FluentAssertions.Specs/Execution/AssertionScope.ContextDataSpecs.cs +++ /dev/null @@ -1,68 +0,0 @@ -using FluentAssertions.Execution; -using Xunit; - -namespace FluentAssertions.Specs.Execution; - -/// -/// The chaining API specs. -/// -public partial class AssertionScopeSpecs -{ - [Fact] - public void Get_value_when_key_is_present() - { - // Arrange - var scope = new AssertionScope(); - scope.AddNonReportable("SomeKey", "SomeValue"); - scope.AddNonReportable("SomeOtherKey", "SomeOtherValue"); - - // Act - var value = scope.Get("SomeKey"); - - // Assert - value.Should().Be("SomeValue"); - } - - [Fact] - public void Get_default_value_when_key_is_not_present() - { - // Arrange - var scope = new AssertionScope(); - - // Act - var value = scope.Get("SomeKey"); - - // Assert - value.Should().Be(0); - } - - [Fact] - public void Get_default_value_when_nullable_value_is_null() - { - // Arrange - var scope = new AssertionScope(); - - int? someValue = null; - scope.AddNonReportable("SomeKey", someValue); - - // Act - var value = scope.Get("SomeKey"); - - // Assert - value.Should().Be(0); - } - - [Fact] - public void Value_should_be_of_requested_type() - { - // Arrange - var scope = new AssertionScope(); - scope.AddNonReportable("SomeKey", "SomeValue"); - - // Act - var value = scope.Get("SomeKey"); - - // Assert - value.Should().BeOfType(); - } -} diff --git a/Tests/FluentAssertions.Specs/Execution/AssertionScope.MessageFormatingSpecs.cs b/Tests/FluentAssertions.Specs/Execution/AssertionScope.MessageFormatingSpecs.cs deleted file mode 100644 index 4dcd1d7af9..0000000000 --- a/Tests/FluentAssertions.Specs/Execution/AssertionScope.MessageFormatingSpecs.cs +++ /dev/null @@ -1,522 +0,0 @@ -using System; -using System.Collections.Generic; -using FluentAssertions.Execution; -using Xunit; -using Xunit.Sdk; - -namespace FluentAssertions.Specs.Execution; - -/// -/// The message formatting specs. -/// -public partial class AssertionScopeSpecs -{ - [Fact] - public void When_the_same_failure_is_handled_twice_or_more_it_should_still_report_it_once() - { - // Arrange - var scope = new AssertionScope(); - - AssertionScope.Current.FailWith("Failure"); - AssertionScope.Current.FailWith("Failure"); - - using (var nestedScope = new AssertionScope()) - { - nestedScope.FailWith("Failure"); - nestedScope.FailWith("Failure"); - } - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().Throw() - .Which.Message.Should().Contain("Failure", Exactly.Times(4)); - } - - [InlineData("foo")] - [InlineData("{}")] - [Theory] - public void Message_should_use_the_name_of_the_scope_as_context(string context) - { - // Act - Action act = () => - { - using var _ = new AssertionScope(context); - new[] { 1, 2, 3 }.Should().Equal(3, 2, 1); - }; - - // Assert - act.Should().Throw() - .WithMessage($"Expected {context} to be equal to*"); - } - - [Fact] - public void Message_should_use_the_lazy_name_of_the_scope_as_context() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(new Lazy(() => "lazy foo")); - new[] { 1, 2, 3 }.Should().Equal(3, 2, 1); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expected lazy foo to be equal to*"); - } - - [Fact] - public void Nested_scopes_use_the_name_of_their_outer_scope_as_context() - { - // Act - Action act = () => - { - using var outerScope = new AssertionScope("outer"); - using var innerScope = new AssertionScope("inner"); - new[] { 1, 2, 3 }.Should().Equal(3, 2, 1); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expected outer/inner to be equal to*"); - } - - [Fact] - public void The_inner_scope_is_used_when_the_outer_scope_does_not_have_a_context() - { - // Act - Action act = () => - { - using var outerScope = new AssertionScope(); - using var innerScope = new AssertionScope("inner"); - new[] { 1, 2, 3 }.Should().Equal(3, 2, 1); - }; - - // Assert - act.Should().Throw() - .WithMessage("Expected inner to be equal to*"); - } - - [Fact] - public void Message_should_contain_each_unique_failed_assertion_seperately() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - var values = new Dictionary(); - values.Should().ContainKey(0); - values.Should().ContainKey(1); - }; - - // Assert - act.Should().Throw() - .WithMessage( - "Expected * to contain key 0.\n" + - "Expected * to contain key 1.\n"); - } - - [Fact] - public void Message_should_contain_the_same_failed_assertion_seperately_if_called_multiple_times() - { - // Act - Action act = () => - { - using var _ = new AssertionScope(); - var values = new List(); - values.Should().ContainSingle(); - values.Should().ContainSingle(); - }; - - // Assert - act.Should().Throw() - .WithMessage( - "Expected * to contain a single item, but the collection is empty.\n" + - "Expected * to contain a single item, but the collection is empty.\n"); - } - - [Fact] - public void Because_reason_should_keep_parentheses_in_arguments_as_literals() - { - // Act - Action act = () => 1.Should().Be(2, "can't use these in becauseArgs: {0} {1}", "{", "}"); - - // Assert - act.Should().Throw() - .WithMessage("*because can't use these in becauseArgs: { }*"); - } - - [Fact] - public void Because_reason_should_ignore_undefined_arguments() - { - // Act - object[] becauseArgs = null; - - // ReSharper disable once FormatStringProblem - Action act = () => 1.Should().Be(2, "it should still work", becauseArgs); - - // Assert - act.Should().Throw() - .WithMessage("*because it should still work*"); - } - - [Fact] - public void Because_reason_should_threat_parentheses_as_literals_if_no_arguments_are_defined() - { - // Act -#pragma warning disable CA2241 - // ReSharper disable once FormatStringProblem - Action act = () => 1.Should().Be(2, "use of {} is okay if there are no because arguments"); -#pragma warning restore CA2241 - - // Assert - act.Should().Throw() - .WithMessage("*because use of {} is okay if there are no because arguments*"); - } - - [Fact] - public void Because_reason_should_inform_about_invalid_parentheses_with_a_default_message() - { - // Act -#pragma warning disable CA2241 - // ReSharper disable once FormatStringProblem - Action act = () => 1.Should().Be(2, "use of {} is considered invalid in because parameter with becauseArgs", - "additional becauseArgs argument"); -#pragma warning restore CA2241 - - // Assert - act.Should().Throw() - .WithMessage( - "*because message 'use of {} is considered invalid in because parameter with becauseArgs' could not be formatted with string.Format*"); - } - - [Fact] - public void Message_should_keep_parentheses_in_literal_values() - { - // Act - Action act = () => "{foo}".Should().Be("{bar}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected string to be \"{bar}\", but \"{foo}\" differs near*"); - } - - [Fact] - public void Message_should_contain_literal_value_if_marked_with_double_parentheses() - { - // Arrange - var scope = new AssertionScope("context"); - - AssertionScope.Current.FailWith("{{empty}}"); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage("{empty}*"); - } - - [InlineData("\r")] - [InlineData("\\r")] - [InlineData("\\\r")] - [InlineData("\\\\r")] - [InlineData("\\\\\r")] - [InlineData("\n")] - [InlineData("\\n")] - [InlineData("\\\n")] - [InlineData("\\\\n")] - [InlineData("\\\\\n")] - [Theory] - public void Message_should_not_have_modified_carriage_return_or_line_feed_control_characters(string str) - { - // Arrange - var scope = new AssertionScope(); - - AssertionScope.Current.FailWith(str); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage(str); - } - - [InlineData("\r")] - [InlineData("\\r")] - [InlineData("\\\r")] - [InlineData(@"\\r")] - [InlineData("\\\\\r")] - [InlineData("\n")] - [InlineData("\\n")] - [InlineData("\\\n")] - [InlineData(@"\\n")] - [InlineData("\\\\\n")] - [Theory] - public void Message_should_not_have_modified_carriage_return_or_line_feed_control_characters_in_supplied_arguments(string str) - { - // Arrange - var scope = new AssertionScope(); - - AssertionScope.Current.FailWith(@"\{0}\A", str); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage("\\\"" + str + "\"\\A*"); - } - - [Fact] - public void Message_should_not_have_trailing_backslashes_removed_from_subject() - { - // Arrange / Act - Action act = () => "A\\".Should().Be("A"); - - // Assert - act.Should().Throw() - .WithMessage("""* near "\" *"""); - } - - [Fact] - public void Message_should_not_have_trailing_backslashes_removed_from_expectation() - { - // Arrange / Act - Action act = () => "A".Should().Be("A\\"); - - // Assert - act.Should().Throw() - .WithMessage("""* to be "A\" *"""); - } - - [Fact] - public void Message_should_have_named_placeholder_be_replaced_by_reportable_value() - { - // Arrange - var scope = new AssertionScope(); - scope.AddReportable("MyKey", "MyValue"); - - AssertionScope.Current.FailWith("{MyKey}"); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage("MyValue*"); - } - - [Fact] - public void Message_should_have_named_placeholders_be_replaced_by_reportable_values() - { - // Arrange - var scope = new AssertionScope(); - scope.AddReportable("SomeKey", "SomeValue"); - scope.AddReportable("AnotherKey", "AnotherValue"); - - AssertionScope.Current.FailWith("{SomeKey}{AnotherKey}"); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage("SomeValueAnotherValue*"); - } - - [Fact] - public void Message_should_have_reportable_values_appended_at_the_end() - { - // Arrange - var scope = new AssertionScope(); - scope.AddReportable("SomeKey", "SomeValue"); - scope.AddReportable("AnotherKey", "AnotherValue"); - - AssertionScope.Current.FailWith("{SomeKey}{AnotherKey}"); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage("*With SomeKey:\nSomeValue\nWith AnotherKey:\nAnotherValue"); - } - - [Fact] - public void Message_should_not_have_nonreportable_values_appended_at_the_end() - { - // Arrange - var scope = new AssertionScope(); - scope.AddNonReportable("SomeKey", "SomeValue"); - - AssertionScope.Current.FailWith("{SomeKey}"); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .Which.Message.Should().NotContain("With SomeKey:\nSomeValue"); - } - - [Fact] - public void Message_should_have_named_placeholder_be_replaced_by_nonreportable_value() - { - // Arrange - var scope = new AssertionScope(); - scope.AddNonReportable("SomeKey", "SomeValue"); - - AssertionScope.Current.FailWith("{SomeKey}"); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage("SomeValue"); - } - - [Fact] - public void Deferred_reportable_values_should_not_be_calculated_in_absence_of_failures() - { - // Arrange - var scope = new AssertionScope(); - var deferredValueInvoked = false; - - scope.AddReportable("MyKey", () => - { - deferredValueInvoked = true; - - return "MyValue"; - }); - - // Act - scope.Dispose(); - - // Assert - deferredValueInvoked.Should().BeFalse(); - } - - [Fact] - public void Message_should_have_named_placeholder_be_replaced_by_defered_reportable_value() - { - // Arrange - var scope = new AssertionScope(); - var deferredValueInvoked = false; - - scope.AddReportable("MyKey", () => - { - deferredValueInvoked = true; - - return "MyValue"; - }); - - AssertionScope.Current.FailWith("{MyKey}"); - - // Act - Action act = scope.Dispose; - - // Assert - act.Should().ThrowExactly() - .WithMessage("MyValue*\n\nWith MyKey:\nMyValue\n"); - - deferredValueInvoked.Should().BeTrue(); - } - - [Fact] - public void Message_should_start_with_the_defined_expectation() - { - // Act - Action act = () => Execute.Assertion - .WithExpectation("Expectations are the root ") - .ForCondition(false) - .FailWith("of disappointment"); - - // Assert - act.Should().Throw() - .WithMessage("Expectations are the root of disappointment"); - } - - [Fact] - public void Message_should_start_with_the_defined_expectation_and_arguments() - { - // Act - Action act = () => Execute.Assertion - .WithExpectation("Expectations are the {0} ", "root") - .ForCondition(false) - .FailWith("of disappointment"); - - // Assert - act.Should().Throw() - .WithMessage("Expectations are the \"root\" of disappointment"); - } - - [Fact] - public void Message_should_contain_object_as_context_if_identifier_can_not_be_resolved() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(false) - .FailWith("Expected {context}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected object"); - } - - [Fact] - public void Message_should_contain_the_fallback_value_as_context_if_identifier_can_not_be_resolved() - { - // Act - Action act = () => Execute.Assertion - .ForCondition(false) - .FailWith("Expected {context:fallback}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected fallback"); - } - - [Fact] - public void Message_should_contain_the_default_identifier_as_context_if_identifier_can_not_be_resolved() - { - // Act - Action act = () => Execute.Assertion - .WithDefaultIdentifier("identifier") - .ForCondition(false) - .FailWith("Expected {context}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected identifier"); - } - - [Fact] - public void Message_should_contain_the_reason_as_defined() - { - // Act - Action act = () => Execute.Assertion - .BecauseOf("because reasons") - .FailWith("Expected{reason}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected because reasons"); - } - - [Fact] - public void Message_should_contain_the_reason_as_defined_with_arguments() - { - // Act - Action act = () => Execute.Assertion - .BecauseOf("because {0}", "reasons") - .FailWith("Expected{reason}"); - - // Assert - act.Should().Throw() - .WithMessage("Expected because reasons"); - } -} diff --git a/Tests/FluentAssertions.Specs/Execution/AssertionScope.ScopedFormatters.cs b/Tests/FluentAssertions.Specs/Execution/AssertionScopeSpecs.ScopedFormatters.cs similarity index 100% rename from Tests/FluentAssertions.Specs/Execution/AssertionScope.ScopedFormatters.cs rename to Tests/FluentAssertions.Specs/Execution/AssertionScopeSpecs.ScopedFormatters.cs diff --git a/Tests/FluentAssertions.Specs/Execution/AssertionScopeSpecs.cs b/Tests/FluentAssertions.Specs/Execution/AssertionScopeSpecs.cs index 931c352e65..440efa3152 100644 --- a/Tests/FluentAssertions.Specs/Execution/AssertionScopeSpecs.cs +++ b/Tests/FluentAssertions.Specs/Execution/AssertionScopeSpecs.cs @@ -24,7 +24,7 @@ public void When_disposed_it_should_throw_any_failures() // Arrange var scope = new AssertionScope(); - AssertionScope.Current.FailWith("Failure1"); + AssertionChain.GetOrCreate().FailWith("Failure1"); // Act Action act = scope.Dispose; @@ -46,7 +46,7 @@ public void When_disposed_it_should_throw_any_failures_and_properly_format_using // Arrange var scope = new AssertionScope(); - AssertionScope.Current.FailWith("Failure{0}", 1); + AssertionChain.GetOrCreate().FailWith("Failure{0}", 1); // Act Action act = scope.Dispose; @@ -69,7 +69,7 @@ public void When_lazy_version_is_not_disposed_it_should_not_execute_fail_reason_ var scope = new AssertionScope(); bool failReasonCalled = false; - AssertionScope.Current + AssertionChain.GetOrCreate() .ForCondition(true) .FailWith(() => { @@ -91,7 +91,9 @@ public void When_lazy_version_is_disposed_it_should_throw_any_failures_and_prope // Arrange var scope = new AssertionScope(); - AssertionScope.Current.FailWith(() => new FailReason("Failure{0}", 1)); + AssertionChain + .GetOrCreate() + .FailWith(() => new FailReason("Failure{0}", 1)); // Act Action act = scope.Dispose; @@ -113,14 +115,14 @@ public void When_multiple_scopes_are_nested_it_should_throw_all_failures_from_th // Arrange var scope = new AssertionScope(); - AssertionScope.Current.FailWith("Failure1"); + AssertionChain.GetOrCreate().FailWith("Failure1"); - using (var nestedScope = new AssertionScope()) + using (new AssertionScope()) { - nestedScope.FailWith("Failure2"); + AssertionChain.GetOrCreate().FailWith("Failure2"); using var deeplyNestedScope = new AssertionScope(); - deeplyNestedScope.FailWith("Failure3"); + AssertionChain.GetOrCreate().FailWith("Failure3"); } // Act @@ -143,14 +145,14 @@ public void When_a_nested_scope_is_discarded_its_failures_should_also_be_discard // Arrange var scope = new AssertionScope(); - AssertionScope.Current.FailWith("Failure1"); + AssertionChain.GetOrCreate().FailWith("Failure1"); - using (var nestedScope = new AssertionScope()) + using (new AssertionScope()) { - nestedScope.FailWith("Failure2"); + AssertionChain.GetOrCreate().FailWith("Failure2"); using var deeplyNestedScope = new AssertionScope(); - deeplyNestedScope.FailWith("Failure3"); + AssertionChain.GetOrCreate().FailWith("Failure3"); deeplyNestedScope.Discard(); } @@ -170,7 +172,7 @@ public void When_a_nested_scope_is_discarded_its_failures_should_also_be_discard } [Fact] - public async Task When_using_AssertionScope_across_thread_boundaries_it_should_work() + public async Task When_using_a_scope_across_thread_boundaries_it_should_work() { using var semaphore = new SemaphoreSlim(0, 1); await Task.WhenAll(SemaphoreYieldAndWait(semaphore), SemaphoreYieldAndRelease(semaphore)); @@ -196,10 +198,10 @@ private static async Task SemaphoreYieldAndRelease(SemaphoreSlim semaphore) public void When_custom_strategy_used_respect_its_behavior() { // Arrange - var scope = new AssertionScope(new FailWithStupidMessageAssertionStrategy()); + using var _ = new AssertionScope(new FailWithStupidMessageAssertionStrategy()); // Act - Action act = () => scope.FailWith("Failure 1"); + Action act = () => AssertionChain.GetOrCreate().FailWith("Failure 1"); // Assert act.Should().ThrowExactly() @@ -240,7 +242,7 @@ public void When_using_a_custom_strategy_it_should_include_failure_messages_of_a public void When_nested_scope_is_disposed_it_passes_reports_to_parent_scope() { // Arrange/Act - using var outerScope = new AssertionScope(); + var outerScope = new AssertionScope(); outerScope.AddReportable("outerReportable", "foo"); using (var innerScope = new AssertionScope()) @@ -248,8 +250,13 @@ public void When_nested_scope_is_disposed_it_passes_reports_to_parent_scope() innerScope.AddReportable("innerReportable", "bar"); } + AssertionChain.GetOrCreate().FailWith("whatever reason"); + + Action act = () => outerScope.Dispose(); + // Assert - outerScope.Get("innerReportable").Should().Be("bar"); + act.Should().Throw() + .Which.Message.Should().Match("Whatever reason*outerReportable*foo*innerReportable*bar*"); } [Fact] @@ -286,6 +293,25 @@ public void Formatting_options_passed_to_inner_assertion_scopes() .Which.Should().Contain("Maximum recursion depth of 1 was reached"); } + [Fact] + public void Multiple_named_scopes_will_prefix_the_caller_identifier() + { + // Arrange + var nonEmptyList = new List([1, 2]); + + // Act + Action act = () => + { + using var scope1 = new AssertionScope("Test1"); + using var scope2 = new AssertionScope("Test2"); + nonEmptyList.Should().BeEmpty(); + }; + + // Assert + act.Should().Throw() + .WithMessage("Expected Test1/Test2/nonEmptyList to be empty*"); + } + public class CustomAssertionStrategy : IAssertionStrategy { private readonly List failureMessages = []; @@ -326,12 +352,12 @@ public void HandleFailure(string message) internal class FailWithStupidMessageAssertionStrategy : IAssertionStrategy { - public IEnumerable FailureMessages => new string[0]; + public IEnumerable FailureMessages => Array.Empty(); public void HandleFailure(string message) => Services.ThrowException("Good luck with understanding what's going on!"); - public IEnumerable DiscardFailures() => new string[0]; + public IEnumerable DiscardFailures() => Array.Empty(); public void ThrowIfAny(IDictionary context) { diff --git a/Tests/FluentAssertions.Specs/Execution/CallerIdentifierSpecs.cs b/Tests/FluentAssertions.Specs/Execution/CallerIdentificationSpecs.cs similarity index 93% rename from Tests/FluentAssertions.Specs/Execution/CallerIdentifierSpecs.cs rename to Tests/FluentAssertions.Specs/Execution/CallerIdentificationSpecs.cs index c508ade7c9..235b19d327 100644 --- a/Tests/FluentAssertions.Specs/Execution/CallerIdentifierSpecs.cs +++ b/Tests/FluentAssertions.Specs/Execution/CallerIdentificationSpecs.cs @@ -14,36 +14,37 @@ // ReSharper disable RedundantStringInterpolation namespace FluentAssertions.Specs.Execution { - public class CallerIdentifierSpecs + public class CallerIdentificationSpecs { [Fact] - public void When_namespace_is_exactly_System_caller_should_be_unknown() + public void Types_in_the_system_namespace_are_excluded_from_identification() { // Act - Action act = () => SystemNamespaceClass.DetermineCallerIdentityInNamespace(); + Action act = () => SystemNamespaceClass.AssertAgainstFailure(); // Assert - act.Should().Throw().WithMessage("Expected function to be*"); + act.Should().Throw().WithMessage("Expected object*", + "because a subject in a system namespace should not be ignored by caller identification"); } [Fact] - public void When_namespace_is_nested_under_System_caller_should_be_unknown() + public void Types_in_a_namespace_nested_under_system_are_excluded_from_identification() { // Act - Action act = () => System.Data.NestedSystemNamespaceClass.DetermineCallerIdentityInNamespace(); + Action act = () => System.Data.NestedSystemNamespaceClass.AssertAgainstFailure(); // Assert - act.Should().Throw().WithMessage("Expected function to be*"); + act.Should().Throw().WithMessage("Expected object*"); } [Fact] - public void When_namespace_is_prefixed_with_System_caller_should_be_known() + public void Types_in_a_namespace_prefixed_with_system_are_excluded_from_identification() { // Act - Action act = () => SystemPrefixed.SystemPrefixedNamespaceClass.DetermineCallerIdentityInNamespace(); + Action act = () => SystemPrefixed.SystemPrefixedNamespaceClass.AssertAgainstFailure(); // Assert - act.Should().Throw().WithMessage("Expected actualCaller to be*"); + act.Should().Throw().WithMessage("Expected actualCaller*"); } [Fact] @@ -545,7 +546,7 @@ public void An_object_initializer_preceding_an_assertion_is_not_an_identifier() // Act Action act = () => new { Property = "blah" }.Should().BeNull(); - // Assert + // Assertl act.Should().Throw() .WithMessage("Expected object to be*"); } @@ -570,7 +571,7 @@ 5. Test } [CustomAssertion] - private string GetSubjectId() => AssertionScope.Current.CallerIdentity; + private string GetSubjectId() => AssertionChain.GetOrCreate().CallerIdentifier; } #pragma warning disable IDE0060, RCS1163 // Remove unused parameter @@ -605,10 +606,10 @@ namespace System { public static class SystemNamespaceClass { - public static void DetermineCallerIdentityInNamespace() + public static void AssertAgainstFailure() { - Func actualCaller = () => AssertionScope.Current.CallerIdentity; - actualCaller.Should().BeNull("we want this check to fail for the test"); + object actualCaller = null; + actualCaller.Should().NotBeNull("because we want this to fail and not return the name of the subject"); } } } @@ -617,10 +618,10 @@ namespace SystemPrefixed { public static class SystemPrefixedNamespaceClass { - public static void DetermineCallerIdentityInNamespace() + public static void AssertAgainstFailure() { - Func actualCaller = () => AssertionScope.Current.CallerIdentity; - actualCaller.Should().BeNull("we want this check to fail for the test"); + object actualCaller = null; + actualCaller.Should().NotBeNull("because we want this to fail and return the name of the subject"); } } } @@ -629,10 +630,10 @@ namespace System.Data { public static class NestedSystemNamespaceClass { - public static void DetermineCallerIdentityInNamespace() + public static void AssertAgainstFailure() { - Func actualCaller = () => AssertionScope.Current.CallerIdentity; - actualCaller.Should().BeNull("we want this check to fail for the test"); + object actualCaller = null; + actualCaller.Should().NotBeNull("because we want this to fail and not return the name of the subject"); } } } diff --git a/Tests/FluentAssertions.Specs/Execution/GivenSelectorSpecs.cs b/Tests/FluentAssertions.Specs/Execution/GivenSelectorSpecs.cs index d9808ab3f2..3209ccddbb 100644 --- a/Tests/FluentAssertions.Specs/Execution/GivenSelectorSpecs.cs +++ b/Tests/FluentAssertions.Specs/Execution/GivenSelectorSpecs.cs @@ -14,7 +14,7 @@ public void A_consecutive_subject_should_be_selected() string value = string.Empty; // Act - Execute.Assertion + AssertionChain.GetOrCreate() .ForCondition(true) .Given(() => "First selector") .Given(_ => value = "Second selector"); @@ -30,7 +30,7 @@ public void After_a_failed_condition_a_consecutive_subject_should_be_ignored() string value = string.Empty; // Act - Execute.Assertion + AssertionChain.GetOrCreate() .ForCondition(false) .Given(() => "First selector") .Given(_ => value = "Second selector"); @@ -43,7 +43,7 @@ public void After_a_failed_condition_a_consecutive_subject_should_be_ignored() public void A_consecutive_condition_should_be_evaluated() { // Act / Assert - Execute.Assertion + AssertionChain.GetOrCreate() .ForCondition(true) .Given(() => "Subject") .ForCondition(_ => true) @@ -54,7 +54,7 @@ public void A_consecutive_condition_should_be_evaluated() public void After_a_failed_condition_a_consecutive_condition_should_be_ignored() { // Act - Action act = () => Execute.Assertion + Action act = () => AssertionChain.GetOrCreate() .ForCondition(false) .Given(() => "Subject") .ForCondition(_ => throw new ApplicationException()) @@ -68,7 +68,7 @@ public void After_a_failed_condition_a_consecutive_condition_should_be_ignored() public void When_continuing_an_assertion_chain_it_fails_with_a_message_after_selecting_the_subject() { // Act - Action act = () => Execute.Assertion + Action act = () => AssertionChain.GetOrCreate() .ForCondition(true) .Given(() => "First") .FailWith("First selector") @@ -85,7 +85,7 @@ public void When_continuing_an_assertion_chain_it_fails_with_a_message_after_sel public void When_continuing_an_assertion_chain_it_fails_with_a_message_with_arguments_after_selecting_the_subject() { // Act - Action act = () => Execute.Assertion + Action act = () => AssertionChain.GetOrCreate() .ForCondition(true) .Given(() => "First") .FailWith("{0} selector", "First") @@ -102,7 +102,7 @@ public void When_continuing_an_assertion_chain_it_fails_with_a_message_with_argu public void When_continuing_an_assertion_chain_it_fails_with_a_message_with_argument_selectors_after_selecting_the_subject() { // Act - Action act = () => Execute.Assertion + Action act = () => AssertionChain.GetOrCreate() .ForCondition(true) .Given(() => "First") .FailWith("{0} selector", _ => "First") @@ -123,7 +123,7 @@ public void When_continuing_a_failed_assertion_chain_consecutive_failure_message { using var _ = new AssertionScope(); - Execute.Assertion + AssertionChain.GetOrCreate() .Given(() => "First") .FailWith("First selector") .Then @@ -143,7 +143,7 @@ public void When_continuing_a_failed_assertion_chain_consecutive_failure_message { using var _ = new AssertionScope(); - Execute.Assertion + AssertionChain.GetOrCreate() .Given(() => "First") .FailWith("{0} selector", "First") .Then @@ -163,7 +163,7 @@ public void When_continuing_a_failed_assertion_chain_consecutive_failure_message { using var _ = new AssertionScope(); - Execute.Assertion + AssertionChain.GetOrCreate() .Given(() => "First") .FailWith("{0} selector", _ => "First") .Then @@ -179,10 +179,13 @@ public void When_continuing_a_failed_assertion_chain_consecutive_failure_message public void The_failure_message_should_be_preceded_by_the_expectation_after_selecting_a_subject() { // Act - Action act = () => Execute.Assertion - .WithExpectation("Expectation ") - .Given(() => "Subject") - .FailWith("Failure"); + Action act = () => + { + AssertionChain.GetOrCreate() + .WithExpectation("Expectation ", chain => chain + .Given(() => "Subject") + .FailWith("Failure")); + }; // Assert act.Should().Throw() @@ -194,12 +197,14 @@ public void The_failure_message_should_not_be_preceded_by_the_expectation_after_selecting_a_subject_and_clearing_the_expectation() { // Act - Action act = () => Execute.Assertion - .WithExpectation("Expectation ") - .Given(() => "Subject") - .ClearExpectation() - .Then - .FailWith("Failure"); + Action act = () => + { + AssertionChain.GetOrCreate() + .WithExpectation("Expectation ", chain => chain + .Given(() => "Subject")) + .Then + .FailWith("Failure"); + }; // Assert act.Should().Throw() @@ -210,38 +215,15 @@ public void public void Clearing_the_expectation_does_not_affect_a_successful_assertion() { // Act - bool result = Execute.Assertion - .WithExpectation("Expectation ") - .Given(() => "Don't care") - .ForCondition(_ => true) - .FailWith("Should not fail") - .Then - .ClearExpectation(); - - // Assert - result.Should().BeTrue(); - } - - [Fact] - public void Clearing_the_expectation_does_not_affect_a_failed_assertion() - { - // Act - using var scope = new AssertionScope(); + var assertionChain = AssertionChain.GetOrCreate(); - bool result = Execute.Assertion - .WithExpectation("Expectation ") - .Given(() => "Don't care") - .ForCondition(_ => false) - .FailWith("Should fail") - .Then - .ClearExpectation(); - - scope.Discard(); + assertionChain + .WithExpectation("Expectation ", chain => chain + .Given(() => "Don't care") + .ForCondition(_ => true) + .FailWith("Should not fail")); // Assert - if (result) - { - throw new XunitException("the assertion failed and should return false"); - } + assertionChain.Succeeded.Should().BeTrue(); } } diff --git a/Tests/FluentAssertions.Specs/Formatting/FormatterSpecs.cs b/Tests/FluentAssertions.Specs/Formatting/FormatterSpecs.cs index 4d6853ee72..e38556fc56 100644 --- a/Tests/FluentAssertions.Specs/Formatting/FormatterSpecs.cs +++ b/Tests/FluentAssertions.Specs/Formatting/FormatterSpecs.cs @@ -211,32 +211,32 @@ public void When_the_object_is_a_generic_type_without_custom_string_representati act.Should().Throw() .WithMessage( """ - Expected stuff to be equal to + Expected stuff to be equal to { FluentAssertions.Specs.Formatting.FormatterSpecs+Stuff`1[[System.Int32*]] { - Children = {1, 2, 3, 4}, - Description = "Stuff_1", + Children = {1, 2, 3, 4}, + Description = "Stuff_1", StuffId = 1 - }, + }, FluentAssertions.Specs.Formatting.FormatterSpecs+Stuff`1[[System.Int32*]] { - Children = {1, 2, 3, 4}, - Description = "WRONG_DESCRIPTION", + Children = {1, 2, 3, 4}, + Description = "WRONG_DESCRIPTION", StuffId = 2 } - }, but + }, but { FluentAssertions.Specs.Formatting.FormatterSpecs+Stuff`1[[System.Int32*]] { - Children = {1, 2, 3, 4}, - Description = "Stuff_1", + Children = {1, 2, 3, 4}, + Description = "Stuff_1", StuffId = 1 - }, + }, FluentAssertions.Specs.Formatting.FormatterSpecs+Stuff`1[[System.Int32*]] { - Children = {1, 2, 3, 4}, - Description = "Stuff_2", + Children = {1, 2, 3, 4}, + Description = "Stuff_2", StuffId = 2 } } differs at index 1. @@ -254,13 +254,13 @@ public void When_the_object_is_a_user_defined_type_it_should_show_the_name_on_th // Assert act.Should().Throw() - .Which.Message.Should().Be( + .Which.Message.Should().Match( """ Expected stuff to be , but found FluentAssertions.Specs.Formatting.FormatterSpecs+StuffRecord { - RecordChildren = {10, 20, 30, 40}, - RecordDescription = "description", - RecordId = 42, + RecordChildren = {10, 20, 30, 40},* + RecordDescription = "description",* + RecordId = 42,* SingleChild = FluentAssertions.Specs.Formatting.FormatterSpecs+ChildRecord { ChildRecordId = 24 @@ -293,18 +293,18 @@ public void When_the_object_is_an_anonymous_type_it_should_show_the_properties_r act.Should().Throw() .Which.Message.Should().Be( """ - Expected stuff to be + Expected stuff to be { - Children = {10, 20, 30, 40}, - SingleChild = + Children = {10, 20, 30, 40}, + SingleChild = { ChildId = 4 } - }, but found + }, but found { - Children = {1, 2, 3, 4}, - Description = "absent", - SingleChild = + Children = {1, 2, 3, 4}, + Description = "absent", + SingleChild = { ChildId = 8 } @@ -345,13 +345,13 @@ public void When_the_object_is_a_list_of_anonymous_type_it_should_show_the_prope // Assert act.Should().Throw() - .Which.Message.Should().StartWith( + .Which.Message.Should().Match( """ - Expected stuff to be a collection with 1 item(s), but + Expected stuff to be a collection with 1 item(s), but* { { Description = "absent" - }, + },* { Description = "absent" } @@ -360,17 +360,17 @@ contains 1 item(s) more than { { - ComplexChildren = + ComplexChildren =* { { Property = "hello" - }, + },* { Property = "goodbye" } } } - }. + }.* """); } @@ -403,19 +403,19 @@ public void When_the_object_is_a_tuple_it_should_show_the_properties_recursively // Assert act.Should().Throw() - .Which.Message.Should().Be( + .Which.Message.Should().Match( """ - Expected stuff to be equal to + Expected stuff to be equal to* { - Item1 = 2, - Item2 = "WRONG_DESCRIPTION", + Item1 = 2,* + Item2 = "WRONG_DESCRIPTION",* Item3 = {4, 5, 6, 7} - }, but found + }, but found* { - Item1 = 1, - Item2 = "description", + Item1 = 1,* + Item2 = "description",* Item3 = {1, 2, 3, 4} - }. + }.* """); } @@ -439,16 +439,16 @@ public void When_the_object_is_a_record_it_should_show_the_properties_recursivel // Assert act.Should().Throw() - .Which.Message.Should().Be( + .Which.Message.Should().Match( """ - Expected stuff to be + Expected stuff to be* { RecordDescription = "WRONG_DESCRIPTION" }, but found FluentAssertions.Specs.Formatting.FormatterSpecs+StuffRecord { - RecordChildren = {4, 5, 6, 7}, - RecordDescription = "descriptive", - RecordId = 9, + RecordChildren = {4, 5, 6, 7},* + RecordDescription = "descriptive",* + RecordId = 9,* SingleChild = FluentAssertions.Specs.Formatting.FormatterSpecs+ChildRecord { ChildRecordId = 80 @@ -1193,7 +1193,7 @@ public void When_defining_a_custom_enumerable_value_formatter_it_should_respect_ str.Should().Match(Environment.NewLine + "{*FluentAssertions*FormatterSpecs+CustomClass" + Environment.NewLine + " {" + Environment.NewLine + - " IntProperty = 1, " + Environment.NewLine + + " IntProperty = 1," + Environment.NewLine + " StringProperty = " + Environment.NewLine + " },*…1 more…*}*"); } diff --git a/Tests/FluentAssertions.Specs/OccurrenceConstraintSpecs.cs b/Tests/FluentAssertions.Specs/OccurrenceConstraintSpecs.cs index 307099e068..7024a699dd 100644 --- a/Tests/FluentAssertions.Specs/OccurrenceConstraintSpecs.cs +++ b/Tests/FluentAssertions.Specs/OccurrenceConstraintSpecs.cs @@ -49,7 +49,7 @@ public class OccurrenceConstraintSpecs public void Occurrence_constraint_passes(OccurrenceConstraint constraint, int occurrences) { // Act / Assert - Execute.Assertion + AssertionChain.GetOrCreate() .ForConstraint(constraint, occurrences) .FailWith(""); } @@ -96,7 +96,7 @@ public void Occurrence_constraint_passes(OccurrenceConstraint constraint, int oc public void Occurrence_constraint_fails(OccurrenceConstraint constraint, int occurrences) { // Act - Action act = () => Execute.Assertion + Action act = () => AssertionChain.GetOrCreate() .ForConstraint(constraint, occurrences) .FailWith($"Expected occurrence to be {constraint.Mode} {constraint.ExpectedCount}, but it was {occurrences}"); diff --git a/Tests/FluentAssertions.Specs/Primitives/ObjectAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Primitives/ObjectAssertionSpecs.cs index e83f7a0394..e57f61e5d8 100644 --- a/Tests/FluentAssertions.Specs/Primitives/ObjectAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Primitives/ObjectAssertionSpecs.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using FluentAssertions.Execution; using FluentAssertions.Primitives; using Xunit; @@ -84,7 +85,7 @@ public bool Equals(SomeClass x, SomeClass y) internal class SomeClassAssertions : ObjectAssertions { public SomeClassAssertions(SomeClass value) - : base(value) + : base(value, AssertionChain.GetOrCreate()) { } } diff --git a/Tests/FluentAssertions.Specs/Primitives/ReferenceTypeAssertionsSpecs.cs b/Tests/FluentAssertions.Specs/Primitives/ReferenceTypeAssertionsSpecs.cs index 191ea2e41e..54f2c74587 100644 --- a/Tests/FluentAssertions.Specs/Primitives/ReferenceTypeAssertionsSpecs.cs +++ b/Tests/FluentAssertions.Specs/Primitives/ReferenceTypeAssertionsSpecs.cs @@ -43,10 +43,10 @@ public void When_two_different_objects_are_expected_to_be_the_same_it_should_fai .Should().Throw() .WithMessage( """ - Expected subject to refer to + Expected subject to refer to { UserName = "JohnDoe" - } because they are the same, but found + } because they are the same, but found { Name = "John Doe" }. @@ -61,7 +61,7 @@ public void When_a_derived_class_has_longer_formatting_than_the_base_class() act.Should().Throw() .WithMessage( """ - Expected subject to be empty, but found at least one item + Expected subject to be empty, but found at least one item { FluentAssertions.Specs.Primitives.Complex { @@ -454,7 +454,7 @@ public void Should_throw_a_helpful_error_when_accidentally_using_equals() public class ReferenceTypeAssertionsDummy : ReferenceTypeAssertions { public ReferenceTypeAssertionsDummy(object subject) - : base(subject) + : base(subject, AssertionChain.GetOrCreate()) { } diff --git a/Tests/FluentAssertions.Specs/Primitives/SimpleTimeSpanAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Primitives/SimpleTimeSpanAssertionSpecs.cs index 1e2a8a56d8..0cf1b14ae1 100644 --- a/Tests/FluentAssertions.Specs/Primitives/SimpleTimeSpanAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Primitives/SimpleTimeSpanAssertionSpecs.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions.Execution; using FluentAssertions.Extensions; using FluentAssertions.Primitives; using Xunit; @@ -165,7 +166,7 @@ public void When_asserting_value_to_be_equal_to_different_value_it_should_fail() public void A_null_is_not_equal_to_another_value() { // Arrange - var subject = new SimpleTimeSpanAssertions(null); + var subject = new SimpleTimeSpanAssertions(null, AssertionChain.GetOrCreate()); TimeSpan expected = 2.Seconds(); // Act diff --git a/Tests/FluentAssertions.Specs/Primitives/StringComparisonSpecs.cs b/Tests/FluentAssertions.Specs/Primitives/StringComparisonSpecs.cs index 5ece34c048..abbc42f90b 100644 --- a/Tests/FluentAssertions.Specs/Primitives/StringComparisonSpecs.cs +++ b/Tests/FluentAssertions.Specs/Primitives/StringComparisonSpecs.cs @@ -264,15 +264,11 @@ public void When_comparing_strings_for_not_containing_any_equals_it_should_ignor [CulturedFact("tr-TR")] public void When_formatting_reason_arguments_it_should_ignore_culture() { - // Arrange - var scope = new AssertionScope(); - // Act - scope.BecauseOf("{0}", 1.234) - .FailWith("{reason}"); + Action act = () => 1.Should().Be(2, "{0}", 1.234); // Assert - scope.Invoking(e => e.Dispose()).Should().Throw() + act.Should().Throw() .WithMessage("*1.234*", "it should always use . as decimal separator"); } diff --git a/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs index 50ec7d4f01..debf5e2c06 100644 --- a/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/AssemblyAssertionSpecs.cs @@ -165,7 +165,7 @@ public void When_an_assembly_is_referencing_null_it_should_throw() public class DefineType { [Fact] - public void When_an_assembly_defines_a_type_and_Should_DefineType_is_asserted_it_should_succeed() + public void Can_find_a_specific_type() { // Arrange var thisAssembly = GetType().Assembly; @@ -179,6 +179,22 @@ public void When_an_assembly_defines_a_type_and_Should_DefineType_is_asserted_it act.Should().NotThrow(); } + [Fact] + public void Can_continue_assertions_on_the_found_type() + { + // Arrange + var thisAssembly = GetType().Assembly; + + // Act + Action act = () => thisAssembly + .Should().DefineType(GetType().Namespace, typeof(WellKnownClassWithAttribute).Name) + .Which.Should().BeDecoratedWith(); + + // Assert + act.Should().Throw() + .WithMessage("Expected*WellKnownClassWithAttribute*decorated*SerializableAttribute*not found."); + } + [Fact] public void When_an_assembly_does_not_define_a_type_and_Should_DefineType_is_asserted_it_should_fail_with_a_useful_message() diff --git a/Tests/FluentAssertions.Specs/Specialized/ExecutionTimeAssertionsSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/ExecutionTimeAssertionsSpecs.cs index d1a591ba61..d0f632baa4 100644 --- a/Tests/FluentAssertions.Specs/Specialized/ExecutionTimeAssertionsSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/ExecutionTimeAssertionsSpecs.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using FluentAssertions.Execution; using FluentAssertions.Extensions; using FluentAssertions.Specialized; using Xunit; @@ -580,7 +581,7 @@ public void When_asserting_on_null_execution_it_should_throw() ExecutionTime executionTime = null; // Act - Func act = () => new ExecutionTimeAssertions(executionTime); + Func act = () => new ExecutionTimeAssertions(executionTime, AssertionChain.GetOrCreate()); // Assert act.Should().Throw() diff --git a/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs index 087c9b2d51..2d5a53994f 100644 --- a/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs @@ -201,7 +201,7 @@ public async Task When_it_completes_in_time_and_async_result_is_not_expected_it_ // Assert await action.Should().ThrowAsync() - .WithMessage("Expected testSubject to be 42, but found 99."); + .WithMessage("Expected testSubject.Result to be 42, but found 99."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs index df75d2f527..5ea7158732 100644 --- a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs @@ -76,6 +76,29 @@ public async Task When_task_completes_fast_it_should_succeed() await action.Should().NotThrowAsync(); } + [Fact] + public async Task Can_chain_another_assertion_on_the_result_of_the_async_operation() + { + // Arrange + var timer = new FakeClock(); + var taskFactory = new TaskCompletionSource(); + + // Act + Func action = async () => + { + Func> func = () => taskFactory.Task; + + (await func.Should(timer).CompleteWithinAsync(100.Milliseconds())) + .Which.Should().Be(42); + }; + + taskFactory.SetResult(42); + timer.Complete(); + + // Assert + await action.Should().NotThrowAsync(); + } + [Fact] public async Task When_task_completes_and_result_is_not_expected_it_should_fail() { @@ -120,7 +143,7 @@ public async Task When_task_completes_and_async_result_is_not_expected_it_should timer.Complete(); // Assert - await action.Should().ThrowAsync().WithMessage("Expected funcSubject to be 42, but found 99."); + await action.Should().ThrowAsync().WithMessage("Expected funcSubject.Result to be 42, but found 99."); } [Fact] @@ -185,7 +208,7 @@ public async Task When_task_does_not_complete_the_result_extension_does_not_hang // Assert var assertionTask = action.Should().ThrowAsync() - .WithMessage("Expected*to complete within 100ms.*Expected*to be 2, but found 0."); + .WithMessage("Expected*to complete within 100ms."); await Awaiting(() => assertionTask).Should().CompleteWithinAsync(200.Seconds()); } @@ -261,6 +284,29 @@ public async Task When_task_does_not_throw_it_should_succeed() await action.Should().NotThrowAsync(); } + [Fact] + public async Task Can_chain_another_assertion_on_the_result_of_the_async_operation() + { + // Arrange + var timer = new FakeClock(); + var taskFactory = new TaskCompletionSource(); + + // Act + Func action = async () => + { + Func> func = () => taskFactory.Task; + + (await func.Should(timer).NotThrowAsync()) + .Which.Should().Be(10); + }; + + taskFactory.SetResult(20); + timer.Complete(); + + // Assert + await action.Should().ThrowAsync().WithMessage("*func.Result to be 10*"); + } + [Fact] public async Task When_task_throws_it_should_fail() { diff --git a/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs index 84a2bf8dc2..997f09fb29 100644 --- a/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Types/MethodBaseAssertionSpecs.cs @@ -389,7 +389,7 @@ public void When_asserting_a_private_member_is_protected_it_throws_with_a_useful // Assert act.Should().Throw() .WithMessage( - "Expected method PrivateMethod to be Protected because we want to test the error message, but it is " + + "Expected method TestClass.PrivateMethod to be Protected because we want to test the error message, but it is " + "Private."); } @@ -426,7 +426,7 @@ public void When_asserting_a_protected_member_is_public_it_throws_with_a_useful_ // Assert act.Should().Throw() .WithMessage( - "Expected method set_ProtectedSetProperty to be Public because we want to test the error message, but it" + + "Expected method TestClass.set_ProtectedSetProperty to be Public because we want to test the error message, but it" + " is Protected."); } @@ -463,7 +463,7 @@ public void When_asserting_a_public_member_is_internal_it_throws_with_a_useful_m // Assert act.Should().Throw() .WithMessage( - "Expected method get_PublicGetProperty to be Internal because we want to test the error message, but it" + + "Expected method TestClass.get_PublicGetProperty to be Internal because we want to test the error message, but it" + " is Public."); } @@ -495,7 +495,7 @@ public void When_asserting_an_internal_member_is_protectedInternal_it_throws_wit // Assert act.Should().Throw() .WithMessage( - "Expected method InternalMethod to be ProtectedInternal because we want to test the error message, but" + + "Expected method TestClass.InternalMethod to be ProtectedInternal because we want to test the error message, but" + " it is Internal."); } @@ -526,7 +526,7 @@ public void When_asserting_a_protected_internal_member_is_private_it_throws_with // Assert act.Should().Throw() .WithMessage( - "Expected method ProtectedInternalMethod to be Private because we want to test the error message, but it is " + + "Expected method TestClass.ProtectedInternalMethod to be Private because we want to test the error message, but it is " + "ProtectedInternal."); } @@ -604,7 +604,7 @@ public void When_asserting_a_private_member_is_not_private_it_throws_with_a_usef // Assert act.Should().Throw() - .WithMessage("Expected method PrivateMethod not to be Private*because we want to test the error message*"); + .WithMessage("Expected method TestClass.PrivateMethod not to be Private*because we want to test the error message*"); } [Fact] @@ -638,7 +638,7 @@ public void When_asserting_a_protected_member_is_not_protected_it_throws_with_a_ // Assert act.Should().Throw() .WithMessage( - "Expected method set_ProtectedSetProperty not to be Protected*because we want to test the error message*"); + "Expected method TestClass.set_ProtectedSetProperty not to be Protected*because we want to test the error message*"); } [Fact] @@ -686,7 +686,7 @@ public void When_asserting_a_public_member_is_not_public_it_throws_with_a_useful // Assert act.Should().Throw() - .WithMessage("Expected method get_PublicGetProperty not to be Public*because we want to test the error message*"); + .WithMessage("Expected method TestClass.get_PublicGetProperty not to be Public*because we want to test the error message*"); } [Fact] @@ -716,7 +716,7 @@ public void When_asserting_an_internal_member_is_not_internal_it_throws_with_a_u // Assert act.Should().Throw() - .WithMessage("Expected method InternalMethod not to be Internal*because we want to test the error message*"); + .WithMessage("Expected method TestClass.InternalMethod not to be Internal*because we want to test the error message*"); } [Fact] @@ -747,7 +747,7 @@ public void When_asserting_a_protected_internal_member_is_not_protected_internal // Assert act.Should().Throw() .WithMessage( - "Expected method ProtectedInternalMethod not to be ProtectedInternal*because we want to test the error message*"); + "Expected method TestClass.ProtectedInternalMethod not to be ProtectedInternal*because we want to test the error message*"); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs index e4479a345e..5d247992e8 100644 --- a/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Types/PropertyInfoAssertionSpecs.cs @@ -19,8 +19,7 @@ public void When_asserting_that_a_property_is_virtual_and_it_is_then_it_succeeds PropertyInfo propertyInfo = typeof(ClassWithAllPropertiesVirtual).GetRuntimeProperty("PublicVirtualProperty"); // Act - Action act = () => - propertyInfo.Should().BeVirtual(); + Action act = () => propertyInfo.Should().BeVirtual(); // Assert act.Should().NotThrow(); @@ -34,13 +33,12 @@ public void When_asserting_that_a_property_is_virtual_and_it_is_not_then_it_fail typeof(ClassWithNonVirtualPublicProperties).GetRuntimeProperty("PublicNonVirtualProperty"); // Act - Action act = () => - propertyInfo.Should().BeVirtual("we want to test the error {0}", "message"); + Action act = () => propertyInfo.Should().BeVirtual("we want to test the error {0}", "message"); // Assert act.Should().Throw() .WithMessage( - "Expected property String FluentAssertions*ClassWithNonVirtualPublicProperties.PublicNonVirtualProperty" + + "Expected property ClassWithNonVirtualPublicProperties.PublicNonVirtualProperty" + " to be virtual because we want to test the error message," + " but it is not."); } @@ -173,8 +171,8 @@ public void When_asserting_a_property_is_decorated_with_attribute_and_it_is_not_ // Assert act.Should().Throw() - .WithMessage("Expected property String " + - "FluentAssertions*ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.PublicProperty to be decorated with " + + .WithMessage("Expected property " + + "ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.PublicProperty to be decorated with " + "FluentAssertions*DummyPropertyAttribute because we want to test the error message, but that attribute was not found."); } @@ -194,7 +192,7 @@ public void // Assert act.Should().Throw() .WithMessage( - "Expected property String FluentAssertions*ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.PublicProperty to be decorated with " + + "Expected property ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.PublicProperty to be decorated with " + "FluentAssertions*DummyPropertyAttribute because we want to test the error message," + " but that attribute was not found."); } @@ -295,7 +293,7 @@ public void When_asserting_a_readonly_property_is_writable_it_fails_with_useful_ action .Should().Throw() .WithMessage( - "Expected propertyInfo ReadOnlyProperty to have a setter because we want to test the error message."); + "Expected property ClassWithProperties.ReadOnlyProperty to have a setter because we want to test the error message."); } [Fact] @@ -381,7 +379,7 @@ public void When_asserting_a_writeonly_property_is_readable_it_fails_with_useful action .Should().Throw() .WithMessage( - "Expected property WriteOnlyProperty to have a getter because we want to test the error message, but it does not."); + "Expected property *WriteOnlyProperty to have a getter because we want to test the error message, but it does not."); } [Fact] @@ -427,8 +425,8 @@ public void When_asserting_a_readwrite_property_is_not_writable_it_fails_with_us // Assert action .Should().Throw() - .WithMessage( - "Expected propertyInfo ReadWriteProperty not to have a setter because we want to test the error message."); + .WithMessage("Did not expect property ClassWithReadOnlyProperties.ReadWriteProperty" + + " to have a setter because we want to test the error message."); } [Fact] @@ -443,8 +441,8 @@ public void When_asserting_a_writeonly_property_is_not_writable_it_fails_with_us // Assert action .Should().Throw() - .WithMessage( - "Expected propertyInfo WriteOnlyProperty not to have a setter because we want to test the error message."); + .WithMessage("Did not expect property ClassWithProperties.WriteOnlyProperty" + + " to have a setter because we want to test the error message."); } [Fact] @@ -459,7 +457,7 @@ public void When_subject_is_null_not_be_writable_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected property not to have a setter *failure message*, but propertyInfo is ."); + .WithMessage("Expected propertyInfo not to have a setter *failure message*, but it is ."); } } @@ -477,8 +475,8 @@ public void When_asserting_a_readonly_property_is_not_readable_it_fails_with_use // Assert action .Should().Throw() - .WithMessage( - "Expected propertyInfo ReadOnlyProperty not to have a getter because we want to test the error message."); + .WithMessage("Did not expect property ClassWithReadOnlyProperties.ReadOnlyProperty " + + "to have a getter because we want to test the error message."); } [Fact] @@ -493,8 +491,8 @@ public void When_asserting_a_readwrite_property_is_not_readable_it_fails_with_us // Assert action .Should().Throw() - .WithMessage( - "Expected propertyInfo ReadWriteProperty not to have a getter because we want to test the error message."); + .WithMessage("Did not expect property ClassWithReadOnlyProperties.ReadWriteProperty " + + "to have a getter because we want to test the error message."); } [Fact] @@ -553,8 +551,8 @@ public void When_asserting_a_private_read_public_write_property_is_public_readab // Assert action.Should().Throw() - .WithMessage( - "Expected method get_WritePrivateReadProperty to be Public because we want to test the error message, but it is Private."); + .WithMessage("Expected getter of property ClassWithProperties.WritePrivateReadProperty " + + "to be Public because we want to test the error message, but it is Private*"); } [Fact] @@ -572,7 +570,7 @@ public void Do_not_the_check_access_modifier_when_the_property_is_not_readable() // Assert action.Should().Throw() - .WithMessage("Expected property WriteOnlyProperty to have a getter, but it does not."); + .WithMessage("Expected property ClassWithProperties.WriteOnlyProperty to have a getter, but it does not."); } [Fact] @@ -587,7 +585,7 @@ public void When_subject_is_null_be_readable_with_accessmodifier_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected property to be Public *failure message*, but propertyInfo is ."); + .WithMessage("Expected propertyInfo to be Public *failure message*, but it is ."); } [Fact] @@ -633,8 +631,8 @@ public void When_asserting_a_private_write_public_read_property_is_public_writab // Assert action.Should().Throw() - .WithMessage( - "Expected method set_ReadPrivateWriteProperty to be Public because we want to test the error message, but it is Private."); + .WithMessage("Expected setter of property ClassWithProperties.ReadPrivateWriteProperty " + + "to be Public because we want to test the error message, but it is Private."); } [Fact] @@ -652,7 +650,7 @@ public void Do_not_the_check_access_modifier_when_the_property_is_not_writable() // Assert action.Should().Throw() - .WithMessage("Expected propertyInfo ReadOnlyProperty to have a setter."); + .WithMessage("Expected property ClassWithProperties.ReadOnlyProperty to have a setter."); } [Fact] @@ -667,7 +665,7 @@ public void When_subject_is_null_be_writable_with_accessmodifier_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected property to be Public *failure message*, but propertyInfo is ."); + .WithMessage("Expected propertyInfo to be Public *failure message*, but it is ."); } [Fact] @@ -712,8 +710,8 @@ public void When_asserting_a_String_property_returns_an_Int32_it_throw_with_a_us // Assert action.Should().Throw() - .WithMessage("Expected Type of property StringProperty to be System.Int32 because we want to test the error " + - "message, but it is System.String."); + .WithMessage("Expected type of property ClassWithProperties.StringProperty" + + " to be System.Int32 because we want to test the error message, but it is System.String."); } [Fact] @@ -773,7 +771,7 @@ public void When_asserting_a_String_property_returnsOfT_an_Int32_it_throw_with_a // Assert action.Should().Throw() - .WithMessage("Expected Type of property StringProperty to be System.Int32 because we want to test the error " + + .WithMessage("Expected type of property ClassWithProperties.StringProperty to be System.Int32 because we want to test the error " + "message, but it is System.String."); } } @@ -804,8 +802,8 @@ public void When_asserting_a_String_property_does_not_return_a_String_it_throw_w // Assert action.Should().Throw() - .WithMessage("Expected Type of property StringProperty not to be*String*because we want to test the error " + - "message, but it is."); + .WithMessage("Expected type of property ClassWithProperties.StringProperty" + + " not to be System.String because we want to test the error message, but it is."); } [Fact] @@ -842,7 +840,7 @@ public void When_asserting_property_type_is_not_null_it_should_throw() public class NotReturnOfT { [Fact] - public void When_asserting_a_String_property_does_not_returnOfT_an_Int32_it_succeeds() + public void Can_validate_the_type_of_a_property() { // Arrange PropertyInfo propertyInfo = typeof(ClassWithProperties).GetRuntimeProperty("StringProperty"); @@ -855,7 +853,7 @@ public void When_asserting_a_String_property_does_not_returnOfT_an_Int32_it_succ } [Fact] - public void When_asserting_a_String_property_does_not_returnsOfT_a_String_it_throw_with_a_useful_message() + public void When_asserting_a_string_property_does_not_returnsOfT_a_String_it_throw_with_a_useful_message() { // Arrange PropertyInfo propertyInfo = typeof(ClassWithProperties).GetRuntimeProperty("StringProperty"); @@ -865,7 +863,7 @@ public void When_asserting_a_String_property_does_not_returnsOfT_a_String_it_thr // Assert action.Should().Throw() - .WithMessage("Expected Type of property StringProperty not to be*String*because we want to test the error " + + .WithMessage("Expected type of property ClassWithProperties.StringProperty not to be*String*because we want to test the error " + "message, but it is."); } } diff --git a/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorAssertionSpecs.cs index cf3dca9c6c..650b56fd00 100644 --- a/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Types/PropertyInfoSelectorAssertionSpecs.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions.Execution; using FluentAssertions.Types; using Xunit; using Xunit.Sdk; @@ -53,9 +54,9 @@ public void .WithMessage("Expected all selected properties" + " to be virtual because we want to test the error message," + " but the following properties are not virtual:*" + - "String FluentAssertions*ClassWithNonVirtualPublicProperties.PublicNonVirtualProperty*" + - "String FluentAssertions*ClassWithNonVirtualPublicProperties.InternalNonVirtualProperty*" + - "String FluentAssertions*ClassWithNonVirtualPublicProperties.ProtectedNonVirtualProperty"); + "ClassWithNonVirtualPublicProperties.PublicNonVirtualProperty*" + + "ClassWithNonVirtualPublicProperties.InternalNonVirtualProperty*" + + "ClassWithNonVirtualPublicProperties.ProtectedNonVirtualProperty"); } } @@ -159,9 +160,9 @@ public void .WithMessage("Expected all selected properties to be decorated with" + " FluentAssertions*DummyPropertyAttribute because we want to test the error message," + " but the following properties are not:*" + - "String FluentAssertions*ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.PublicProperty*" + - "String FluentAssertions*ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.InternalProperty*" + - "String FluentAssertions*ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.ProtectedProperty"); + "ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.PublicProperty*" + + "ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.InternalProperty*" + + "ClassWithPropertiesThatAreNotDecoratedWithDummyAttribute.ProtectedProperty"); } } @@ -236,8 +237,8 @@ public void When_a_read_only_property_is_expected_to_be_writable_it_should_throw .WithMessage( "Expected all selected properties to have a setter because we want to test the error message, " + "but the following properties do not:*" + - "String FluentAssertions*ClassWithReadOnlyProperties.ReadOnlyProperty*" + - "String FluentAssertions*ClassWithReadOnlyProperties.ReadOnlyProperty2"); + "ClassWithReadOnlyProperties.ReadOnlyProperty*" + + "ClassWithReadOnlyProperties.ReadOnlyProperty2"); } [Fact] @@ -271,8 +272,8 @@ public void When_a_writable_property_is_expected_to_be_read_only_it_should_throw .WithMessage( "Expected selected properties to not have a setter because we want to test the error message, " + "but the following properties do:*" + - "String FluentAssertions*ClassWithWritableProperties.ReadWriteProperty*" + - "String FluentAssertions*ClassWithWritableProperties.ReadWriteProperty2"); + "ClassWithWritableProperties.ReadWriteProperty*" + + "ClassWithWritableProperties.ReadWriteProperty2"); } [Fact] @@ -295,7 +296,7 @@ public class Miscellaneous public void When_accidentally_using_equals_it_should_throw_a_helpful_error() { // Arrange - var someObject = new PropertyInfoSelectorAssertions(); + var someObject = new PropertyInfoSelectorAssertions(AssertionChain.GetOrCreate()); // Act var action = () => someObject.Equals(null); diff --git a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveExplicitConversionOperator.cs b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveExplicitConversionOperator.cs index 5eedb90a94..89fa79cdd3 100644 --- a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveExplicitConversionOperator.cs +++ b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveExplicitConversionOperator.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions.Common; using Xunit; using Xunit.Sdk; @@ -19,15 +20,30 @@ public void When_asserting_a_type_has_an_explicit_conversion_operator_which_it_d var sourceType = typeof(TypeWithConversionOperators); var targetType = typeof(byte); + // Act / Assert + type.Should() + .HaveExplicitConversionOperator(sourceType, targetType) + .Which.Should() + .NotBeNull(); + } + + [Fact] + public void Can_chain_an_additional_assertion_on_the_implicit_conversion_operator() + { + // Arrange + var type = typeof(TypeWithConversionOperators); + var sourceType = typeof(TypeWithConversionOperators); + var targetType = typeof(byte); + // Act - Action act = () => - type.Should() - .HaveExplicitConversionOperator(sourceType, targetType) - .Which.Should() - .NotBeNull(); + Action act = () => type + .Should().HaveExplicitConversionOperator(sourceType, targetType) + .Which.Should().HaveAccessModifier(CSharpAccessModifier.Private); // Assert - act.Should().NotThrow(); + act.Should().Throw() + .WithMessage( + "Expected method explicit operator Byte(TypeWithConversionOperators) to be Private, but it is Public."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveImplicitConversionOperator.cs b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveImplicitConversionOperator.cs index d5ef12601b..f3194619e9 100644 --- a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveImplicitConversionOperator.cs +++ b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveImplicitConversionOperator.cs @@ -1,4 +1,5 @@ using System; +using FluentAssertions.Common; using Xunit; using Xunit.Sdk; @@ -118,6 +119,24 @@ public void When_asserting_a_type_has_an_implicit_conversion_operatorOfT_which_i act.Should().NotThrow(); } + [Fact] + public void Can_chain_an_additional_assertion_on_the_implicit_conversion_operator() + { + // Arrange + var type = typeof(TypeWithConversionOperators); + + // Act + Action act = () => + type.Should() + .HaveImplicitConversionOperator() + .Which.Should() + .HaveAccessModifier(CSharpAccessModifier.Internal); + + // Assert + act.Should().Throw() + .WithMessage("Expected method implicit operator Int32(TypeWithConversionOperators) to be Internal, but it is Public."); + } + [Fact] public void When_asserting_a_type_has_an_implicit_conversion_operatorOfT_which_it_does_not_it_fails() { diff --git a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs index fe7f4cbb6a..543f439ff3 100644 --- a/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs +++ b/Tests/FluentAssertions.Specs/Types/TypeAssertionSpecs.HaveProperty.cs @@ -30,6 +30,21 @@ public void When_asserting_a_type_has_a_property_which_it_does_then_it_succeeds( act.Should().NotThrow(); } + [Fact] + public void The_name_of_the_property_is_passed_to_the_chained_assertion() + { + // Arrange + var type = typeof(ClassWithMembers); + + // Act + Action act = () => type + .Should().HaveProperty(typeof(string), "PrivateWriteProtectedReadProperty") + .Which.Should().NotBeWritable(); + + // Assert + act.Should().Throw("Expected property PrivateWriteProtectedReadProperty not to have a setter."); + } + [Fact] public void When_asserting_a_type_has_a_property_which_it_does_not_it_fails() { @@ -42,7 +57,7 @@ public void When_asserting_a_type_has_a_property_which_it_does_not_it_fails() // Assert act.Should().Throw() - .WithMessage("Expected String *ClassWithNoMembers.PublicProperty to exist *failure message*, but it does not."); + .WithMessage("Expected ClassWithNoMembers to have a property PublicProperty of type String because we want to test the failure message, but it does not."); } [Fact] @@ -58,9 +73,8 @@ public void When_asserting_a_type_has_a_property_which_it_has_with_a_different_t // Assert act.Should().Throw() - .WithMessage( - "Expected String *.ClassWithMembers.PrivateWriteProtectedReadProperty to be of type System.Int32 " + - "*failure message*, but it is not."); + .WithMessage("Expected property PrivateWriteProtectedReadProperty " + + "to be of type System.Int32 because we want to test the failure message, but it is not."); } [Fact] @@ -75,7 +89,7 @@ public void When_subject_is_null_have_property_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected String type.PublicProperty to exist *failure message*, but type is ."); + .WithMessage("Cannot determine if a type has a property named PublicProperty if the type is ."); } [Fact] @@ -184,8 +198,7 @@ public void When_asserting_a_type_does_not_have_a_property_which_it_does_not_it_ var type = typeof(ClassWithoutMembers); // Act - Action act = () => - type.Should().NotHaveProperty("Property"); + Action act = () => type.Should().NotHaveProperty("Property"); // Assert act.Should().NotThrow(); @@ -203,9 +216,7 @@ public void When_asserting_a_type_does_not_have_a_property_which_it_does_it_fail // Assert act.Should().Throw() - .WithMessage( - "Expected String *.ClassWithMembers.PrivateWriteProtectedReadProperty to not exist *failure message*" + - ", but it does."); + .WithMessage("Did not expect ClassWithMembers to have a property PrivateWriteProtectedReadProperty because we want to test the failure message, but it does."); } [Fact] @@ -220,7 +231,7 @@ public void When_subject_is_null_not_have_property_should_fail() // Assert act.Should().Throw() - .WithMessage("Expected type.PublicProperty to not exist *failure message*, but type is ."); + .WithMessage("Cannot determine if a type has an unexpected property named PublicProperty if the type is ."); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Xml/XDocumentAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Xml/XDocumentAssertionSpecs.cs index f09023363c..0c7270b0b0 100644 --- a/Tests/FluentAssertions.Specs/Xml/XDocumentAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Xml/XDocumentAssertionSpecs.cs @@ -990,6 +990,25 @@ public void When_asserting_document_has_root_element_with_ns_but_it_does_not_it_ "Expected theDocument to have root element \"{http://www.example.com/2012/test}unknown\", but found ."); } + [Fact] + public void Can_chain_another_assertion_on_the_root_element() + { + // Arrange + var theDocument = XDocument.Parse( + """ + + + + """); + + // Act + Action act = () => theDocument.Should().HaveRoot("parent").Which.Should().HaveElement("unknownChild"); + + // Assert + act.Should().Throw().WithMessage( + "Expected theDocument/parent to have child element*unknownChild*"); + } + [Fact] public void When_asserting_document_has_root_element_with_ns_but_it_does_not_it_should_fail_with_descriptive_message() { @@ -1036,6 +1055,24 @@ public void When_document_has_the_expected_child_element_it_should_not_throw_and element.Should().BeSameAs(document.Element("parent").Element("child")); } + [Fact] + public void Can_chain_another_assertion_on_the_root_element() + { + // Arrange + var document = XDocument.Parse( + """ + + + + """); + + // Act + var act = () => document.Should().HaveElement("child").Which.Should().HaveElement("grandChild"); + + // Assert + act.Should().Throw().WithMessage("Expected document/child to have child element*grandChild*"); + } + [Fact] public void When_asserting_document_has_root_with_child_element_but_it_does_not_it_should_fail() { diff --git a/qodana.yaml b/qodana.yaml index 3f248f5a33..c4d518d378 100644 --- a/qodana.yaml +++ b/qodana.yaml @@ -33,6 +33,7 @@ exclude: - name: SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault - name: UnusedMemberInSuper.Global - name: ArrangeAccessorOwnerBody + - name: ParameterHidesMember - name: CollectionNeverUpdated.Local paths: - Tests