From 9e4bd68d20308812b527c245deb5a8dc8fc0af36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Gr=C3=BCtzmacher?= Date: Sat, 23 Jul 2022 18:10:24 +0200 Subject: [PATCH 1/2] Added assertions for non-generic TaskCompletionSource --- Src/FluentAssertions/AssertionExtensions.cs | 16 +- .../TaskCompletionSourceAssertions.cs | 162 ++++++-- .../FluentAssertions/net47.verified.txt | 16 +- .../FluentAssertions/net6.0.verified.txt | 24 +- .../netcoreapp2.1.verified.txt | 16 +- .../netcoreapp3.0.verified.txt | 16 +- .../netstandard2.0.verified.txt | 16 +- .../netstandard2.1.verified.txt | 16 +- .../AssertionExtensions.cs | 9 + .../AssertionExtensionsSpecs.cs | 27 +- .../TaskCompletionSourceAssertionSpecs.cs | 356 +++++++++++------- docs/_pages/releases.md | 1 + 12 files changed, 470 insertions(+), 205 deletions(-) diff --git a/Src/FluentAssertions/AssertionExtensions.cs b/Src/FluentAssertions/AssertionExtensions.cs index be02085535..300a86039a 100644 --- a/Src/FluentAssertions/AssertionExtensions.cs +++ b/Src/FluentAssertions/AssertionExtensions.cs @@ -919,6 +919,20 @@ public static IMonitor Monitor(this T eventSource, Func utcNow = return new EventMonitor(eventSource, utcNow ?? (() => DateTime.UtcNow)); } +#endif + +#if NET6_0_OR_GREATER + + /// + /// Returns a object that can be used to assert the + /// current . + /// + [Pure] + public static TaskCompletionSourceAssertions Should(this TaskCompletionSource tcs) + { + return new TaskCompletionSourceAssertions(tcs); + } + #endif /// @@ -1038,7 +1052,7 @@ public static void Should(this SimpleTimeSpanAssertions [Obsolete("You are asserting the 'AndConstraint' itself. Remove the 'Should()' method directly following 'And'", error: true)] - public static void Should(this TaskCompletionSourceAssertions _) + public static void Should(this TaskCompletionSourceAssertionsBase _) { InvalidShouldCall(); } diff --git a/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs b/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs index 00faaf614d..3adc8892a0 100644 --- a/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs +++ b/Src/FluentAssertions/Specialized/TaskCompletionSourceAssertions.cs @@ -8,7 +8,89 @@ namespace FluentAssertions.Specialized; #pragma warning disable CS0659 // Ignore not overriding Object.GetHashCode() #pragma warning disable CA1065 // Ignore throwing NotSupportedException from Equals -public class TaskCompletionSourceAssertions + +#if NET6_0_OR_GREATER +public class TaskCompletionSourceAssertions : TaskCompletionSourceAssertionsBase +{ + private readonly TaskCompletionSource subject; + + public TaskCompletionSourceAssertions(TaskCompletionSource tcs) + : this(tcs, new Clock()) + { + } + + public TaskCompletionSourceAssertions(TaskCompletionSource tcs, IClock clock) + : base(clock) + { + subject = tcs; + } + + /// + /// Asserts that the of the current will complete within the specified time. + /// + /// The allowed time span for the operation. + /// + /// 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 async Task> CompleteWithinAsync( + TimeSpan timeSpan, string because = "", params object[] becauseArgs) + { + var success = Execute.Assertion + .ForCondition(subject is not null) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context} to complete within {0}{reason}, but found .", timeSpan); + + if (success) + { + bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject.Task, timeSpan); + Execute.Assertion + .ForCondition(completesWithinTimeout) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); + } + + return new AndConstraint(this); + } + + /// + /// Asserts that the of the current will not complete within the specified time. + /// + /// The time span to wait for the operation. + /// + /// 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 async Task> NotCompleteWithinAsync( + TimeSpan timeSpan, string because = "", params object[] becauseArgs) + { + var success = Execute.Assertion + .ForCondition(subject is not null) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context} to not complete within {0}{reason}, but found .", timeSpan); + + if (success) + { + bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject.Task, timeSpan); + Execute.Assertion + .ForCondition(!completesWithinTimeout) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context:task} to not complete within {0}{reason}.", timeSpan); + } + + return new AndConstraint(this); + } +} + +#endif + +public class TaskCompletionSourceAssertions : TaskCompletionSourceAssertionsBase { private readonly TaskCompletionSource subject; @@ -18,13 +100,11 @@ public TaskCompletionSourceAssertions(TaskCompletionSource tcs) } public TaskCompletionSourceAssertions(TaskCompletionSource tcs, IClock clock) + : base(clock) { subject = tcs; - Clock = clock ?? throw new ArgumentNullException(nameof(clock)); } - private protected IClock Clock { get; } - /// /// Asserts that the of the current will complete within the specified time. /// @@ -39,27 +119,23 @@ public TaskCompletionSourceAssertions(TaskCompletionSource tcs, IClock clock) public async Task, T>> CompleteWithinAsync( TimeSpan timeSpan, string because = "", params object[] becauseArgs) { - Execute.Assertion + var success = Execute.Assertion .ForCondition(subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to complete within {0}{reason}, but found .", timeSpan); - using var timeoutCancellationTokenSource = new CancellationTokenSource(); - Task completedTask = await Task.WhenAny( - subject.Task, - Clock.DelayAsync(timeSpan, timeoutCancellationTokenSource.Token)); - - if (completedTask == subject.Task) + if (!success) { - timeoutCancellationTokenSource.Cancel(); - await completedTask; + return new AndWhichConstraint, T>(this, default(T)); } + bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject.Task, timeSpan); Execute.Assertion - .ForCondition(completedTask == subject.Task) + .ForCondition(completesWithinTimeout) .BecauseOf(because, becauseArgs) .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); - return new AndWhichConstraint, T>(this, subject.Task.Result); + T result = subject.Task.IsCompleted ? subject.Task.Result : default; + return new AndWhichConstraint, T>(this, result); } /// @@ -73,32 +149,60 @@ public async Task, T>> Comp /// /// Zero or more objects to format using the placeholders in . /// - public async Task NotCompleteWithinAsync( + public async Task>> NotCompleteWithinAsync( TimeSpan timeSpan, string because = "", params object[] becauseArgs) { - Execute.Assertion + var success = Execute.Assertion .ForCondition(subject is not null) .BecauseOf(because, becauseArgs) .FailWith("Did not expect {context} to complete within {0}{reason}, but found .", timeSpan); - using var timeoutCancellationTokenSource = new CancellationTokenSource(); - Task completedTask = await Task.WhenAny( - subject.Task, - Clock.DelayAsync(timeSpan, timeoutCancellationTokenSource.Token)); - - if (completedTask == subject.Task) + if (success) { - timeoutCancellationTokenSource.Cancel(); - await completedTask; + bool completesWithinTimeout = await CompletesWithinTimeoutAsync(subject.Task, timeSpan); + Execute.Assertion + .ForCondition(!completesWithinTimeout) + .BecauseOf(because, becauseArgs) + .FailWith("Did not expect {context:task} to complete within {0}{reason}.", timeSpan); } - Execute.Assertion - .ForCondition(completedTask != subject.Task) - .BecauseOf(because, becauseArgs) - .FailWith("Did not expect {context:task} to complete within {0}{reason}.", timeSpan); + return new AndConstraint>(this); } +} + +/// +/// Implements base functionality for assertions on TaskCompletionSource. +/// +public class TaskCompletionSourceAssertionsBase +{ + protected TaskCompletionSourceAssertionsBase(IClock clock) + { + Clock = clock ?? throw new ArgumentNullException(nameof(clock)); + } + + private protected IClock Clock { get; } /// public override bool Equals(object obj) => throw new NotSupportedException("Calling Equals on Assertion classes is not supported."); + + /// + /// Monitors the specified task whether it completes withing the remaining time span. + /// + private protected async Task CompletesWithinTimeoutAsync(Task target, TimeSpan remainingTime) + { + using var timeoutCancellationTokenSource = new CancellationTokenSource(); + + Task completedTask = + await Task.WhenAny(target, Clock.DelayAsync(remainingTime, timeoutCancellationTokenSource.Token)); + + if (completedTask != target) + { + return false; + } + + // cancel the clock + timeoutCancellationTokenSource.Cancel(); + return true; + } } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt index f2d2ba526e..f4c9cab2a9 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt @@ -43,6 +43,9 @@ namespace FluentAssertions [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { } + [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + + "ly following \'And\'", true)] + public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { } public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { } [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] @@ -135,9 +138,6 @@ namespace FluentAssertions "ly following \'And\'", true)] public static void Should(this FluentAssertions.Primitives.SimpleTimeSpanAssertions _) where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { } - [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + - "ly following \'And\'", true)] - public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertions _) { } public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Collections.Generic.IEnumerable actualValue) { } public static FluentAssertions.Specialized.GenericAsyncFunctionAssertions Should(this System.Func> action) { } public static FluentAssertions.Specialized.FunctionAssertions Should(this System.Func func) { } @@ -2348,13 +2348,17 @@ namespace FluentAssertions.Specialized public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } } - public class TaskCompletionSourceAssertions + public class TaskCompletionSourceAssertionsBase + { + protected TaskCompletionSourceAssertionsBase(FluentAssertions.Common.IClock clock) { } + public override bool Equals(object obj) { } + } + 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 System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public override bool Equals(object obj) { } - public System.Threading.Tasks.Task NotCompleteWithinAsync(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 diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt index 6f8abf4512..b2fc685a80 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt @@ -43,6 +43,9 @@ namespace FluentAssertions [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { } + [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + + "ly following \'And\'", true)] + public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { } public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { } [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] @@ -77,6 +80,7 @@ namespace FluentAssertions public static FluentAssertions.Types.ConstructorInfoAssertions Should(this System.Reflection.ConstructorInfo constructorInfo) { } public static FluentAssertions.Types.MethodInfoAssertions Should(this System.Reflection.MethodInfo methodInfo) { } public static FluentAssertions.Types.PropertyInfoAssertions Should(this System.Reflection.PropertyInfo propertyInfo) { } + public static FluentAssertions.Specialized.TaskCompletionSourceAssertions Should(this System.Threading.Tasks.TaskCompletionSource tcs) { } public static FluentAssertions.Primitives.TimeOnlyAssertions Should(this System.TimeOnly actualValue) { } public static FluentAssertions.Primitives.NullableTimeOnlyAssertions Should(this System.TimeOnly? actualValue) { } public static FluentAssertions.Primitives.SimpleTimeSpanAssertions Should(this System.TimeSpan actualValue) { } @@ -147,9 +151,6 @@ namespace FluentAssertions "ly following \'And\'", true)] public static void Should(this FluentAssertions.Primitives.TimeOnlyAssertions _) where TAssertions : FluentAssertions.Primitives.TimeOnlyAssertions { } - [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + - "ly following \'And\'", true)] - public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertions _) { } public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Collections.Generic.IEnumerable actualValue) { } public static FluentAssertions.Specialized.GenericAsyncFunctionAssertions Should(this System.Func> action) { } public static FluentAssertions.Specialized.FunctionAssertions Should(this System.Func func) { } @@ -2466,13 +2467,24 @@ namespace FluentAssertions.Specialized public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } } - public class TaskCompletionSourceAssertions + 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 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) { } + } + public class TaskCompletionSourceAssertionsBase + { + protected TaskCompletionSourceAssertionsBase(FluentAssertions.Common.IClock clock) { } + public override bool Equals(object obj) { } + } + 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 System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public override bool Equals(object obj) { } - public System.Threading.Tasks.Task NotCompleteWithinAsync(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 diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt index 9e06dba6fd..326829c95c 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt @@ -43,6 +43,9 @@ namespace FluentAssertions [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { } + [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + + "ly following \'And\'", true)] + public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { } public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { } [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] @@ -135,9 +138,6 @@ namespace FluentAssertions "ly following \'And\'", true)] public static void Should(this FluentAssertions.Primitives.SimpleTimeSpanAssertions _) where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { } - [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + - "ly following \'And\'", true)] - public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertions _) { } public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Collections.Generic.IEnumerable actualValue) { } public static FluentAssertions.Specialized.GenericAsyncFunctionAssertions Should(this System.Func> action) { } public static FluentAssertions.Specialized.FunctionAssertions Should(this System.Func func) { } @@ -2348,13 +2348,17 @@ namespace FluentAssertions.Specialized public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } } - public class TaskCompletionSourceAssertions + public class TaskCompletionSourceAssertionsBase + { + protected TaskCompletionSourceAssertionsBase(FluentAssertions.Common.IClock clock) { } + public override bool Equals(object obj) { } + } + 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 System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public override bool Equals(object obj) { } - public System.Threading.Tasks.Task NotCompleteWithinAsync(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 diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt index 26f5978a06..3cf876407b 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt @@ -43,6 +43,9 @@ namespace FluentAssertions [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { } + [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + + "ly following \'And\'", true)] + public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { } public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { } [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] @@ -135,9 +138,6 @@ namespace FluentAssertions "ly following \'And\'", true)] public static void Should(this FluentAssertions.Primitives.SimpleTimeSpanAssertions _) where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { } - [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + - "ly following \'And\'", true)] - public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertions _) { } public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Collections.Generic.IEnumerable actualValue) { } public static FluentAssertions.Specialized.GenericAsyncFunctionAssertions Should(this System.Func> action) { } public static FluentAssertions.Specialized.FunctionAssertions Should(this System.Func func) { } @@ -2348,13 +2348,17 @@ namespace FluentAssertions.Specialized public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } } - public class TaskCompletionSourceAssertions + public class TaskCompletionSourceAssertionsBase + { + protected TaskCompletionSourceAssertionsBase(FluentAssertions.Common.IClock clock) { } + public override bool Equals(object obj) { } + } + 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 System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public override bool Equals(object obj) { } - public System.Threading.Tasks.Task NotCompleteWithinAsync(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 diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt index d64dbc6ef5..37237ba2d8 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt @@ -42,6 +42,9 @@ namespace FluentAssertions [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { } + [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + + "ly following \'And\'", true)] + public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { } public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { } [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] @@ -134,9 +137,6 @@ namespace FluentAssertions "ly following \'And\'", true)] public static void Should(this FluentAssertions.Primitives.SimpleTimeSpanAssertions _) where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { } - [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + - "ly following \'And\'", true)] - public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertions _) { } public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Collections.Generic.IEnumerable actualValue) { } public static FluentAssertions.Specialized.GenericAsyncFunctionAssertions Should(this System.Func> action) { } public static FluentAssertions.Specialized.FunctionAssertions Should(this System.Func func) { } @@ -2300,13 +2300,17 @@ namespace FluentAssertions.Specialized public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } } - public class TaskCompletionSourceAssertions + public class TaskCompletionSourceAssertionsBase + { + protected TaskCompletionSourceAssertionsBase(FluentAssertions.Common.IClock clock) { } + public override bool Equals(object obj) { } + } + 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 System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public override bool Equals(object obj) { } - public System.Threading.Tasks.Task NotCompleteWithinAsync(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 diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt index fdddf3faf8..7cb0960b6b 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt @@ -43,6 +43,9 @@ namespace FluentAssertions [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] public static void Should(this FluentAssertions.Specialized.ExecutionTimeAssertions _) { } + [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + + "ly following \'And\'", true)] + public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertionsBase _) { } public static FluentAssertions.Types.MethodInfoSelectorAssertions Should(this FluentAssertions.Types.MethodInfoSelector methodSelector) { } [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + "ly following \'And\'", true)] @@ -135,9 +138,6 @@ namespace FluentAssertions "ly following \'And\'", true)] public static void Should(this FluentAssertions.Primitives.SimpleTimeSpanAssertions _) where TAssertions : FluentAssertions.Primitives.SimpleTimeSpanAssertions { } - [System.Obsolete("You are asserting the \'AndConstraint\' itself. Remove the \'Should()\' method direct" + - "ly following \'And\'", true)] - public static void Should(this FluentAssertions.Specialized.TaskCompletionSourceAssertions _) { } public static FluentAssertions.Collections.GenericCollectionAssertions Should(this System.Collections.Generic.IEnumerable actualValue) { } public static FluentAssertions.Specialized.GenericAsyncFunctionAssertions Should(this System.Func> action) { } public static FluentAssertions.Specialized.FunctionAssertions Should(this System.Func func) { } @@ -2348,13 +2348,17 @@ namespace FluentAssertions.Specialized public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } } - public class TaskCompletionSourceAssertions + public class TaskCompletionSourceAssertionsBase + { + protected TaskCompletionSourceAssertionsBase(FluentAssertions.Common.IClock clock) { } + public override bool Equals(object obj) { } + } + 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 System.Threading.Tasks.Task, T>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public override bool Equals(object obj) { } - public System.Threading.Tasks.Task NotCompleteWithinAsync(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 diff --git a/Tests/FluentAssertions.Specs/AssertionExtensions.cs b/Tests/FluentAssertions.Specs/AssertionExtensions.cs index 376926cf60..89473dfda7 100644 --- a/Tests/FluentAssertions.Specs/AssertionExtensions.cs +++ b/Tests/FluentAssertions.Specs/AssertionExtensions.cs @@ -33,4 +33,13 @@ public static TaskCompletionSourceAssertions Should(this TaskCompletionSou { return new TaskCompletionSourceAssertions(tcs, clock); } + +#if NET6_0_OR_GREATER + + public static TaskCompletionSourceAssertions Should(this TaskCompletionSource tcs, IClock clock) + { + return new TaskCompletionSourceAssertions(tcs, clock); + } + +#endif } diff --git a/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs b/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs index 0582e9e2f1..ef6d97af10 100644 --- a/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs +++ b/Tests/FluentAssertions.Specs/AssertionExtensionsSpecs.cs @@ -44,10 +44,6 @@ private static bool OverridesEquals(Type t) new object[] { new DateTimeRangeAssertions(default, default, default, default) }, new object[] { new DateTimeOffsetAssertions(default) }, new object[] { new DateTimeOffsetRangeAssertions(default, default, default, default) }, -#if NET6_0_OR_GREATER - new object[] { new DateOnlyAssertions(default) }, - new object[] { new TimeOnlyAssertions(default) }, -#endif new object[] { new ExecutionTimeAssertions(new ExecutionTime(() => { }, () => new StopwatchTimer())) }, new object[] { new GuidAssertions(default) }, new object[] { new MethodInfoSelectorAssertions() }, @@ -56,7 +52,11 @@ private static bool OverridesEquals(Type t) new object[] { new SimpleTimeSpanAssertions(default) }, new object[] { new TaskCompletionSourceAssertions(default) }, new object[] { new TypeSelectorAssertions() }, - new object[] { new EnumAssertions>(default) } + new object[] { new EnumAssertions>(default) }, +#if NET6_0_OR_GREATER + new object[] { new DateOnlyAssertions(default) }, + new object[] { new TimeOnlyAssertions(default) }, +#endif }; [Theory] @@ -77,19 +77,19 @@ public void Guarding_equals_throws(object obj) [InlineData(typeof(DateTimeRangeAssertions))] [InlineData(typeof(DateTimeOffsetAssertions))] [InlineData(typeof(DateTimeOffsetRangeAssertions))] -#if NET6_0_OR_GREATER - [InlineData(typeof(DateOnlyAssertions))] - [InlineData(typeof(TimeOnlyAssertions))] -#endif [InlineData(typeof(ExecutionTimeAssertions))] [InlineData(typeof(GuidAssertions))] [InlineData(typeof(MethodInfoSelectorAssertions))] [InlineData(typeof(NumericAssertions>))] [InlineData(typeof(PropertyInfoSelectorAssertions))] [InlineData(typeof(SimpleTimeSpanAssertions))] - [InlineData(typeof(TaskCompletionSourceAssertions))] + [InlineData(typeof(TaskCompletionSourceAssertionsBase))] [InlineData(typeof(TypeSelectorAssertions))] [InlineData(typeof(EnumAssertions>))] +#if NET6_0_OR_GREATER + [InlineData(typeof(DateOnlyAssertions))] + [InlineData(typeof(TimeOnlyAssertions))] +#endif public void Fake_should_method_throws(Type type) { // Arrange @@ -134,8 +134,13 @@ public void Should_methods_have_a_matching_overload_to_guard_against_chaining_an .Distinct() .Concat(new[] { + // @jnyrup: DateTimeRangeAssertions and DateTimeOffsetRangeAssertions are manually added here, + // because they expose AndConstraints, + // and hence should have a guarding Should(DateTimeRangeAssertions _) overloads, + // but they do not have a regular Should() overload, + // as they are always constructed through the fluent API. typeof(DateTimeRangeAssertions<>), - typeof(DateTimeOffsetRangeAssertions<>) + typeof(DateTimeOffsetRangeAssertions<>), }) .ToList(); diff --git a/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs index 12c823cc69..ca0409a405 100644 --- a/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/TaskCompletionSourceAssertionSpecs.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using FluentAssertions.Extensions; using Xunit; @@ -9,160 +8,261 @@ namespace FluentAssertions.Specs.Specialized; public class TaskCompletionSourceAssertionSpecs { - [Fact] - public async Task When_TCS_completes_in_time_it_should_succeed() +#if NET6_0_OR_GREATER + + public class NonGeneric { - // Arrange - var subject = new TaskCompletionSource(); - var timer = new FakeClock(); + [Fact] + public async Task When_it_completes_in_time_it_should_succeed() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); - // Act - Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds()); - subject.SetResult(true); - timer.Complete(); + // Act + Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds()); + subject.SetResult(); + timer.Complete(); - // Assert - await action.Should().NotThrowAsync(); - } + // Assert + await action.Should().NotThrowAsync(); + } - [Fact] - public async Task When_TCS_completes_in_time_and_result_is_expected_it_should_succeed() - { - // Arrange - var subject = new TaskCompletionSource(); - var timer = new FakeClock(); + [Fact] + public async Task When_it_did_not_complete_in_time_it_should_fail() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); - // Act - Func action = async () => (await subject.Should(timer).CompleteWithinAsync(1.Seconds())).Which.Should().Be(42); - subject.SetResult(42); - timer.Complete(); + // Act + Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); + timer.Complete(); - // Assert - await action.Should().NotThrowAsync(); - } + // Assert + await action.Should().ThrowAsync() + .WithMessage("Expected subject to complete within 1s because test testArg."); + } - [Fact] - public async Task When_TCS_completes_in_time_and_async_result_is_expected_it_should_succeed() - { - // Arrange - var subject = new TaskCompletionSource(); - var timer = new FakeClock(); + [Fact] + public async Task When_it_is_null_it_should_fail() + { + // Arrange + TaskCompletionSource subject = null; - // Act - Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds()).WithResult(42); - subject.SetResult(42); - timer.Complete(); + // Act + Func action = () => subject.Should().CompleteWithinAsync(1.Seconds()); - // Assert - await action.Should().NotThrowAsync(); - } + // Assert + await action.Should().ThrowAsync() + .WithMessage("Expected subject to complete within 1s, but found ."); + } - [Fact] - public async Task When_TCS_completes_in_time_and_result_is_not_expected_it_should_fail() - { - // Arrange - var testSubject = new TaskCompletionSource(); - var timer = new FakeClock(); - - // Act - Func action = async () => (await testSubject.Should(timer).CompleteWithinAsync(1.Seconds())).Which.Should().Be(42); - testSubject.SetResult(99); - timer.Complete(); - - // Assert - await action.Should().ThrowAsync() - .WithMessage("Expected *testSubject* to be 42, but found 99 (difference of 57)."); - } + [Fact] + public async Task When_it_completes_in_time_and_it_is_not_expected_it_should_fail() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); - [Fact] - public async Task When_TCS_completes_in_time_and_async_result_is_not_expected_it_should_fail() - { - // Arrange - var testSubject = new TaskCompletionSource(); - var timer = new FakeClock(); - - // Act - Func action = () => testSubject.Should(timer).CompleteWithinAsync(1.Seconds()).WithResult(42); - testSubject.SetResult(99); - timer.Complete(); - - // Assert - await action.Should().ThrowAsync() - .WithMessage("Expected testSubject to be 42, but found 99."); - } + // Act + Func action = () => subject.Should(timer).NotCompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); + subject.SetResult(); + timer.Complete(); - [Fact] - public async Task When_TCS_did_not_complete_in_time_it_should_fail() - { - // Arrange - var subject = new TaskCompletionSource(); - var timer = new FakeClock(); + // Assert + await action.Should().ThrowAsync().WithMessage("*to not complete within*because test testArg*"); + } - // Act - Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); - timer.Complete(); + [Fact] + public async Task When_it_did_not_complete_in_time_and_it_is_not_expected_it_should_succeed() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); - // Assert - await action.Should().ThrowAsync() - .WithMessage("Expected subject to complete within 1s because test testArg."); - } + // Act + Func action = () => subject.Should(timer).NotCompleteWithinAsync(1.Seconds()); + timer.Complete(); - [Fact] - public async Task When_TCS_is_null_it_should_fail() - { - // Arrange - TaskCompletionSource subject = null; + // Assert + await action.Should().NotThrowAsync(); + } - // Act - Func action = () => subject.Should().CompleteWithinAsync(1.Seconds()); + [Fact] + public async Task When_it_is_null_and_we_validate_to_not_complete_it_should_fail() + { + // Arrange + TaskCompletionSource subject = null; - // Assert - await action.Should().ThrowAsync() - .WithMessage("Expected subject to complete within 1s, but found ."); + // Act + Func action = () => subject.Should().NotCompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Expected subject to not complete within 1s because test testArg, but found ."); + } } +#endif - [Fact] - public async Task When_TCS_completes_in_time_and_it_is_not_expected_it_should_fail() + public class Generic { - // Arrange - var subject = new TaskCompletionSource(); - var timer = new FakeClock(); + [Fact] + public async Task When_it_completes_in_time_it_should_succeed() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); - // Act - Func action = () => subject.Should(timer).NotCompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); - subject.SetResult(true); - timer.Complete(); + // Act + Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds()); + subject.SetResult(true); + timer.Complete(); - // Assert - await action.Should().ThrowAsync().WithMessage("Did not expect*to complete within*because test testArg*"); - } + // Assert + await action.Should().NotThrowAsync(); + } - [Fact] - public async Task When_TCS_did_not_complete_in_time_and_it_is_not_expected_it_should_succeed() - { - // Arrange - var subject = new TaskCompletionSource(); - var timer = new FakeClock(); + [Fact] + public async Task When_it_completes_in_time_and_result_is_expected_it_should_succeed() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); - // Act - Func action = () => subject.Should(timer).NotCompleteWithinAsync(1.Seconds()); - timer.Complete(); + // Act + Func action = async () => (await subject.Should(timer).CompleteWithinAsync(1.Seconds())).Which.Should().Be(42); + subject.SetResult(42); + timer.Complete(); - // Assert - await action.Should().NotThrowAsync(); - } + // Assert + await action.Should().NotThrowAsync(); + } - [Fact] - public async Task When_TCS_is_null_and_we_validate_to_not_complete_it_should_fail() - { - // Arrange - TaskCompletionSource subject = null; + [Fact] + public async Task When_it_completes_in_time_and_async_result_is_expected_it_should_succeed() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); + + // Act + Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds()).WithResult(42); + subject.SetResult(42); + timer.Complete(); + + // Assert + await action.Should().NotThrowAsync(); + } + + [Fact] + public async Task When_it_completes_in_time_and_result_is_not_expected_it_should_fail() + { + // Arrange + var testSubject = new TaskCompletionSource(); + var timer = new FakeClock(); + + // Act + Func action = async () => + (await testSubject.Should(timer).CompleteWithinAsync(1.Seconds())).Which.Should().Be(42); + testSubject.SetResult(99); + timer.Complete(); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Expected *testSubject* to be 42, but found 99 (difference of 57)."); + } + + [Fact] + public async Task When_it_completes_in_time_and_async_result_is_not_expected_it_should_fail() + { + // Arrange + var testSubject = new TaskCompletionSource(); + var timer = new FakeClock(); + + // Act + Func action = () => testSubject.Should(timer).CompleteWithinAsync(1.Seconds()).WithResult(42); + testSubject.SetResult(99); + timer.Complete(); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Expected testSubject to be 42, but found 99."); + } + + [Fact] + public async Task When_it_did_not_complete_in_time_it_should_fail() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); + + // Act + Func action = () => subject.Should(timer).CompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); + timer.Complete(); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Expected subject to complete within 1s because test testArg."); + } + + [Fact] + public async Task When_it_is_null_it_should_fail() + { + // Arrange + TaskCompletionSource subject = null; + + // Act + Func action = () => subject.Should().CompleteWithinAsync(1.Seconds()); + + // Assert + await action.Should().ThrowAsync() + .WithMessage("Expected subject to complete within 1s, but found ."); + } + + [Fact] + public async Task When_it_completes_in_time_and_it_is_not_expected_it_should_fail() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); + + // Act + Func action = () => subject.Should(timer).NotCompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); + subject.SetResult(true); + timer.Complete(); + + // Assert + await action.Should().ThrowAsync().WithMessage("Did not expect*to complete within*because test testArg*"); + } + + [Fact] + public async Task When_it_did_not_complete_in_time_and_it_is_not_expected_it_should_succeed() + { + // Arrange + var subject = new TaskCompletionSource(); + var timer = new FakeClock(); + + // Act + Func action = () => subject.Should(timer).NotCompleteWithinAsync(1.Seconds()); + timer.Complete(); + + // Assert + await action.Should().NotThrowAsync(); + } + + [Fact] + public async Task When_it_is_null_and_we_validate_to_not_complete_it_should_fail() + { + // Arrange + TaskCompletionSource subject = null; - // Act - Func action = () => subject.Should().NotCompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); + // Act + Func action = () => subject.Should().NotCompleteWithinAsync(1.Seconds(), "test {0}", "testArg"); - // Assert - await action.Should().ThrowAsync() - .WithMessage("Did not expect subject to complete within 1s because test testArg, but found ."); + // Assert + await action.Should().ThrowAsync() + .WithMessage("Did not expect subject to complete within 1s because test testArg, but found ."); + } } } diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index ffe878de27..c3e6b9b66e 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -12,6 +12,7 @@ sidebar: ### What's new * Added `ContainInConsecutiveOrder` and `NotContainInConsecutiveOrder` assertions to check if a collection contains items in a specific order and to be consecutive - [#1963](https://github.com/fluentassertions/fluentassertions/pull/1963) * Added `NotCompleteWithinAsync` for assertions on `Task` - [#1967](https://github.com/fluentassertions/fluentassertions/pull/1967) +* Added `CompleteWithinAsync` and `NotCompleteWithinAsync` for non-generic `TaskCompletionSource` (.NET 6 and above) - [#1961](https://github.com/fluentassertions/fluentassertions/pull/1961) * Added a `ParentType` to `IObjectInfo` to help determining the parent in a call to `Using`/`When` constructs - [#1950](https://github.com/fluentassertions/fluentassertions/pull/1950) ### Fixes From 9fce45e23d47d2e418644f298e03cf639398d7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Gr=C3=BCtzmacher?= Date: Sat, 23 Jul 2022 18:11:47 +0200 Subject: [PATCH 2/2] Unified spelling of the changelog --- docs/_pages/releases.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index c3e6b9b66e..a0bd78905d 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -16,19 +16,19 @@ sidebar: * Added a `ParentType` to `IObjectInfo` to help determining the parent in a call to `Using`/`When` constructs - [#1950](https://github.com/fluentassertions/fluentassertions/pull/1950) ### Fixes -* Fix `For`/`Exclude` not excluding properties in objects in a collection - [#1953](https://github.com/fluentassertions/fluentassertions/pull/1953) +* Fixed `For`/`Exclude` not excluding properties in objects in a collection - [#1953](https://github.com/fluentassertions/fluentassertions/pull/1953) ## 6.7.0 ### What's new -* Add `BeDefined` and `NotBeDefined` to assert on existence of an enum value - [#1888](https://github.com/fluentassertions/fluentassertions/pull/1888) +* Added `BeDefined` and `NotBeDefined` to assert on existence of an enum value - [#1888](https://github.com/fluentassertions/fluentassertions/pull/1888) * Added the ability to exclude fields & properties marked as non-browsable in the code editor from structural equality comparisons - [#1807](https://github.com/fluentassertions/fluentassertions/pull/1807) & [#1812](https://github.com/fluentassertions/fluentassertions/pull/1812) * Assertions on the collection types in System.Data (`DataSet.Tables`, `DataTable.Columns`, `DataTable.Rows`) have been restored - [#1812](https://github.com/fluentassertions/fluentassertions/pull/1812) -* Add `For`/`Exclude` to allow exclusion of members inside a collection - [#1782](https://github.com/fluentassertions/fluentassertions/pull/1782) +* Added `For`/`Exclude` to allow exclusion of members inside a collection - [#1782](https://github.com/fluentassertions/fluentassertions/pull/1782) * Added overload for `HaveElement` for `XDocument` and `XElement` to assert on number of XML nodes - [#1880](https://github.com/fluentassertions/fluentassertions/pull/1880) ### Fixes -* Fix the failure message for regex matches (occurrence overload) to include the missing subject - [#1913](https://github.com/fluentassertions/fluentassertions/pull/1913) +* Fixed the failure message for regex matches (occurrence overload) to include the missing subject - [#1913](https://github.com/fluentassertions/fluentassertions/pull/1913) * Fixed `WithArgs` matching too many events when at least one argument matched the expected type - [#1920](https://github.com/fluentassertions/fluentassertions/pull/1920) ## 6.6.0 @@ -61,7 +61,7 @@ sidebar: ### Fixes * Improved the documentation on `BeLowerCased` and `BeUpperCased` for strings with non-alphabetic characters - [#1792](https://github.com/fluentassertions/fluentassertions/pull/1792) * Caller identification does not handle all arguments using `new` - [#1794](https://github.com/fluentassertions/fluentassertions/pull/1794) -* Resolve an issue preventing `HaveAccessModifier` from correctly recognizing internal interfaces and enums - [#1793](https://github.com/fluentassertions/fluentassertions/issues/1793) +* Resolved an issue preventing `HaveAccessModifier` from correctly recognizing internal interfaces and enums - [#1793](https://github.com/fluentassertions/fluentassertions/issues/1793) * Improved tracing for nested `AssertionScope`s - [#1797](https://github.com/fluentassertions/fluentassertions/pull/1797) ### Fixes (Extensibility) @@ -70,9 +70,9 @@ sidebar: ## 6.4.0 ### What's New -* Adding `ThatAreStatic()` and `ThatAreNotStatic()` for filtering in method assertions - [#1740](https://github.com/fluentassertions/fluentassertions/pull/1740) -* Adding new assertions for the `HttpStatusCode` of an `HttpResponseMessage` - [#1737](https://github.com/fluentassertions/fluentassertions/pull/1737) -* Adding non-generic overloads for `WithInnerExceptionExactly` and `WithInnerException` - [#1769](https://github.com/fluentassertions/fluentassertions/pull/1769) +* Added `ThatAreStatic()` and `ThatAreNotStatic()` for filtering in method assertions - [#1740](https://github.com/fluentassertions/fluentassertions/pull/1740) +* Added new assertions for the `HttpStatusCode` of an `HttpResponseMessage` - [#1737](https://github.com/fluentassertions/fluentassertions/pull/1737) +* Added non-generic overloads for `WithInnerExceptionExactly` and `WithInnerException` - [#1769](https://github.com/fluentassertions/fluentassertions/pull/1769) ### Fixes * `ContainItemsAssignableTo` now expects at least one item assignable to `T` - [#1765](https://github.com/fluentassertions/fluentassertions/pull/1765) @@ -86,20 +86,20 @@ sidebar: ## 6.3.0 ### What's New -* Adding `ThatAreAsync()` and `ThatAreNotAsync()` for filtering in method assertions - [#1725](https://github.com/fluentassertions/fluentassertions/pull/1725) -* Adding `ThatAreVirtual()` and `ThatAreNotVirtual()` for filtering in method assertions - [#1744](https://github.com/fluentassertions/fluentassertions/pull/1744) -* Adding collection content to assertion messages for `HaveCountGreaterThan()`, `HaveCountGreaterThanOrEqualTo()`, `HaveCountLessThan()` and `HaveCountLessThanOrEqualTo()` - [#1760](https://github.com/fluentassertions/fluentassertions/pull/1760) +* Added `ThatAreAsync()` and `ThatAreNotAsync()` for filtering in method assertions - [#1725](https://github.com/fluentassertions/fluentassertions/pull/1725) +* Added `ThatAreVirtual()` and `ThatAreNotVirtual()` for filtering in method assertions - [#1744](https://github.com/fluentassertions/fluentassertions/pull/1744) +* Added collection content to assertion messages for `HaveCountGreaterThan()`, `HaveCountGreaterThanOrEqualTo()`, `HaveCountLessThan()` and `HaveCountLessThanOrEqualTo()` - [#1760](https://github.com/fluentassertions/fluentassertions/pull/1760) ### Fixes * Prevent multiple enumeration of `IEnumerable`s in parameter-less `ContainSingle()` - [#1753](https://github.com/fluentassertions/fluentassertions/pull/1753) -* Change `HaveCount()` assertion message order to state expected and actual collection count before dumping its content` - [#1760](https://github.com/fluentassertions/fluentassertions/pull/1760) +* Changed `HaveCount()` assertion message order to state expected and actual collection count before dumping its content` - [#1760](https://github.com/fluentassertions/fluentassertions/pull/1760) * `CompleteWithinAsync` did not take initial sync computation into account when measuring execution time - [1762](https://github.com/fluentassertions/fluentassertions/pull/1762). ## 6.2.0 ### What's New -* Adding new overloads to all `GreaterOrEqualTo` and `LessOrEqualTo` assertions, adding the word `Than` - [#1673](https://github.com/fluentassertions/fluentassertions/pull/1673) +* Added new overloads to all `GreaterOrEqualTo` and `LessOrEqualTo` assertions, adding the word `Than` - [#1673](https://github.com/fluentassertions/fluentassertions/pull/1673) * `BeAsync()` and `NotBeAsync()` are now also available on `MethodInfoSelectorAssertions` - [#1700](https://github.com/fluentassertions/fluentassertions/pull/1700) ### Fixes