Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry pick fixes from develop to master #16

Draft
wants to merge 53 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b28034a
Add 6.13 release notes template
jnyrup Jul 14, 2024
70e1d76
Fix Possible 'System.NullReferenceException' warning for Streams asse…
lg2de Aug 23, 2023
cab751e
Fix Possible 'System.NullReferenceException' warning in GenericCollec…
lg2de Aug 23, 2023
8534c53
Fix Possible 'System.NullReferenceException' warning for Specialized …
lg2de Aug 23, 2023
a8779f4
Fix Possible 'System.NullReferenceException' warning for Xml assertions
lg2de Aug 23, 2023
6159dc7
Fix Possible 'System.NullReferenceException' warning for Types assert…
lg2de Aug 23, 2023
6309fa8
added missing parenthesis in docs
Wake1st Sep 7, 2023
0042108
Fixed formatting error when checking nullable `DateTimeOffset` with `…
rokklobster Sep 22, 2023
ce9e3e0
BeEquivalentTo will now find and can map subject properties that are …
dennisdoomen Aug 16, 2023
a685585
Temporaily accept removed APIs
jnyrup Jul 14, 2024
c131996
Removed the "non-private" part from names since visibility is now use…
dennisdoomen Aug 12, 2023
30ae84f
Pass down the reason to inner `AssertionScope` (#2318)
IT-VBFK Sep 28, 2023
5c68705
Filter out members earlier in selection
jnyrup Sep 25, 2023
dbe59fe
Use List.ToArray instead of Enumerable.ToArray
jnyrup Sep 25, 2023
f220a0d
Avoid intermediate array allocation
jnyrup Sep 25, 2023
d2f6399
Refactor retrieval of properties
jnyrup Sep 27, 2023
f77a6a3
change of "which" to "whose"
mpityo Oct 2, 2023
729a1fc
Add a note to XML-docs that explains the `(Not)ContainEquivalentOf` p…
IT-VBFK Oct 8, 2023
121ea6f
Use targeted methods instead of LINQ
jnyrup Oct 11, 2023
7494d62
Handle comparing an `IDictionary` subject with an `IDictionary<,>` ex…
ITaluone Oct 14, 2023
4e8923e
Pass `FormattingOptions` to inner `AssertionScope` (#2329)
ITaluone Oct 14, 2023
333e6a4
Capitalize true and false in failure messages (#2390)
BusHero Oct 17, 2023
93204ac
Pass Boolean literals as becauseArguments
jnyrup Oct 18, 2023
3948d28
Add release notes for #2393 (#2396)
IT-VBFK Oct 20, 2023
3ddce52
Fix a small typo in release notes
ITaluone Oct 20, 2023
efe4b76
Fix another typo
ITaluone Oct 20, 2023
7299c58
Improve failure message for `NotBeOfType` and `BeReadable`/`BeWritabl…
jnyrup Oct 21, 2023
1284bb8
Do not continue asserting on the concrete exception type when the exc…
IT-VBFK Oct 22, 2023
7ff0541
Guard methods against assertion scope `[Not]HaveExplicit(Property|Met…
IT-VBFK Oct 25, 2023
7885d6e
Remove unecessary call to `Subject.Should()`
jnyrup Oct 22, 2023
e306c59
Ensure all code from the core assembly in a stack trace is ignored by…
dennisdoomen Oct 24, 2023
7bdb3f0
Return type can be `IEnumerable<T>`
ITaluone Oct 23, 2023
9d4fe9e
Type member is never accessed via base type
ITaluone Oct 23, 2023
e11453a
Exclude `private protected` members from `BeEquivalentTo`
jnyrup Oct 29, 2023
69dab01
Improve CC for `GetCSharpAccessModifier(FieldInfo)`
jnyrup Oct 30, 2023
6efd8da
Restore DateTime tips
jnyrup Oct 30, 2023
493f155
Split GenericDictionaryAssertionSpecs (#2424)
vbreuss Nov 1, 2023
a1b9e7d
Split GenericCollectionAssertionOfStringSpecs (#2425)
vbreuss Nov 1, 2023
de7ab52
Ignore EOL TFMs for old netcoreapp
jnyrup Jul 14, 2024
e2b1974
add datetime tips for date parts (#2435)
Meir017 Nov 4, 2023
6ecdf5b
Add EqualityStrategyProvider for `SelfReferenceEquivalencyAssertionOp…
vbreuss Nov 4, 2023
c17d7a2
Avoid possible 'null' assignment in `CallerIdentifier` (#2448)
vbreuss Nov 17, 2023
67b36c2
Fix issue when an empty ArraySegment is a member of a class (#2511)
ITaluone Dec 15, 2023
94dc6ab
Correct null handling when using a custom comparer (#2489)
MartinDemberger Dec 29, 2023
9852592
BeEmpty() materializes IEnumerable<T> only once, even on failure (#2530)
louis-z Jan 5, 2024
9a84b62
fixup d2dbf777 by removing collection expressions
jnyrup Jul 14, 2024
07ad7da
Update release notes (#2541)
louis-z Jan 7, 2024
a524192
Correct spelling in 'Then' property summary xml documentation. (#2594)
PKirch-SE Mar 2, 2024
a8ebb3a
Ensured that nested assertion scopes produce a nested context (#2607)
dennisdoomen Mar 20, 2024
d5a1949
Only allocate Lazy<> and lambda when necessary
jnyrup Mar 24, 2024
89de89e
Simplify deferred allocation with local function
jnyrup Mar 24, 2024
b279879
Make ThrowWithinAsync respect canceled tasks (#2614)
jnyrup Mar 25, 2024
95012e8
Fixed `BeEquivalentTo` when using a custom comparer targeting nullabl…
arocheleau May 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<LangVersion>11.0</LangVersion>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<WarningsNotAsErrors>$(WarningsNotAsErrors);NU1902;NU1903</WarningsNotAsErrors> <!-- disable EOL warnings -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

Expand All @@ -13,7 +14,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'net6.0'">
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisLevel>7.0</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
Expand Down
29 changes: 19 additions & 10 deletions Src/FluentAssertions/CallerIdentifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ public static string DetermineCallerIdentity()
{
var stack = new StackTrace(fNeedFileInfo: true);

var allStackFrames = stack.GetFrames()
.Where(frame => frame is not null && !IsCompilerServices(frame))
.ToArray();
var allStackFrames = GetFrames(stack);

int searchStart = allStackFrames.Length - 1;

Expand All @@ -55,7 +53,8 @@ public static string DetermineCallerIdentity()
if (frame.GetMethod() is not null
&& !IsDynamic(frame)
&& !IsDotNet(frame)
&& !IsCustomAssertion(frame))
&& !IsCustomAssertion(frame)
&& !IsCurrentAssembly(frame))
{
caller = ExtractVariableNameFrom(frame);
break;
Expand All @@ -81,9 +80,7 @@ public StackFrameReference()
{
var stack = new StackTrace();

var allStackFrames = stack.GetFrames()
.Where(frame => frame is not null && !IsCompilerServices(frame))
.ToArray();
var allStackFrames = GetFrames(stack);

int firstUserCodeFrameIndex = 0;

Expand Down Expand Up @@ -114,9 +111,7 @@ internal static IDisposable OverrideStackSearchUsingCurrentScope()

internal static bool OnlyOneFluentAssertionScopeOnCallStack()
{
var allStackFrames = new StackTrace().GetFrames()
.Where(frame => frame is not null && !IsCompilerServices(frame))
.ToArray();
var allStackFrames = GetFrames(new StackTrace());

int firstNonFluentAssertionsStackFrameIndex = Array.FindIndex(
allStackFrames,
Expand Down Expand Up @@ -256,4 +251,18 @@ private static bool IsBooleanLiteral(string candidate)
{
return candidate is "true" or "false";
}

private static StackFrame[] GetFrames(StackTrace stack)
{
var frames = stack.GetFrames();
#if !NET6_0_OR_GREATER
if (frames == null)
{
return Array.Empty<StackFrame>();
}
#endif
return frames
.Where(frame => !IsCompilerServices(frame))
.ToArray();
}
}
100 changes: 61 additions & 39 deletions Src/FluentAssertions/Collections/GenericCollectionAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public AndWhichConstraint<TAssertions, IEnumerable<TExpectation>> AllBeAssignabl
Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected type to be {0}{reason}, ", typeof(TExpectation).FullName)
.ForCondition(Subject.All(x => x is not null))
.ForCondition(Subject!.All(x => x is not null))
.FailWith("but found a null element.")
.Then
.ForCondition(Subject.All(x => typeof(TExpectation).IsAssignableFrom(GetType(x))))
Expand Down Expand Up @@ -226,7 +226,7 @@ public AndWhichConstraint<TAssertions, IEnumerable<TExpectation>> AllBeOfType<TE
Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected type to be {0}{reason}, ", typeof(TExpectation).FullName)
.ForCondition(Subject.All(x => x is not null))
.ForCondition(Subject!.All(x => x is not null))
.FailWith("but found a null element.")
.Then
.ForCondition(Subject.All(x => typeof(TExpectation) == GetType(x)))
Expand Down Expand Up @@ -286,15 +286,16 @@ public AndConstraint<TAssertions> AllBeOfType(Type expectedType, string because
/// </param>
public AndConstraint<TAssertions> BeEmpty(string because = "", params object[] becauseArgs)
{
var singleItemArray = Subject?.Take(1).ToArray();
Execute.Assertion
.BecauseOf(because, becauseArgs)
.WithExpectation("Expected {context:collection} to be empty{reason}, ")
.Given(() => Subject)
.Given(() => singleItemArray)
.ForCondition(subject => subject is not null)
.FailWith("but found <null>.")
.Then
.ForCondition(subject => !subject.Any())
.FailWith("but found {0}.", Subject)
.ForCondition(subject => subject.Length == 0)
.FailWith("but found at least one item {0}.", singleItemArray)
.Then
.ClearExpectation();

Expand Down Expand Up @@ -639,13 +640,14 @@ public AndConstraint<SubsequentOrderingAssertions<T>> BeInDescendingOrder(Func<T
/// </param>
public AndConstraint<TAssertions> BeNullOrEmpty(string because = "", params object[] becauseArgs)
{
var nullOrEmpty = Subject is null || !Subject.Any();
var singleItemArray = Subject?.Take(1).ToArray();
var nullOrEmpty = singleItemArray is null || singleItemArray.Length == 0;

Execute.Assertion.ForCondition(nullOrEmpty)
.BecauseOf(because, becauseArgs)
.FailWith(
"Expected {context:collection} to be null or empty{reason}, but found {0}.",
Subject);
"Expected {context:collection} to be null or empty{reason}, but found at least one item {0}.",
singleItemArray);

return new AndConstraint<TAssertions>((TAssertions)this);
}
Expand Down Expand Up @@ -748,7 +750,7 @@ public AndWhichConstraint<TAssertions, T> Contain(Expression<Func<T, bool>> pred
Func<T, bool> func = predicate.Compile();

Execute.Assertion
.ForCondition(Subject.Any(func))
.ForCondition(Subject!.Any(func))
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:collection} {0} to have an item matching {1}{reason}.", Subject, predicate.Body);

Expand Down Expand Up @@ -786,7 +788,7 @@ public AndConstraint<TAssertions> Contain(IEnumerable<T> expected, string becaus

if (success)
{
IEnumerable<T> missingItems = expectedObjects.Except(Subject);
IEnumerable<T> missingItems = expectedObjects.Except(Subject!);

if (missingItems.Any())
{
Expand All @@ -811,13 +813,18 @@ public AndConstraint<TAssertions> Contain(IEnumerable<T> expected, string becaus
}

/// <summary>
/// Asserts that a collection of objects contains at least one object equivalent to another object.
/// Asserts that at least one element in the collection is equivalent to <paramref name="expectation"/>.
/// </summary>
/// <remarks>
/// Objects within the collection are equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another
/// and the result is equal.
/// <para>
/// <b>Important:</b> You cannot use this method to assert whether a subset of the collection is equivalent to the <paramref name="expectation"/>.
/// This usually means that the expectation is meant to be a <i>single</i> item.
/// </para>
/// <para>
/// By default, objects within the collection are seen as equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects.
/// Notice that actual behavior is determined by the global defaults managed by <see cref="AssertionOptions"/>.
/// </para>
/// </remarks>
/// <param name="expectation">The expected element.</param>
/// <param name="because">
Expand All @@ -834,13 +841,18 @@ public AndWhichConstraint<TAssertions, T> ContainEquivalentOf<TExpectation>(TExp
}

/// <summary>
/// Asserts that a collection of objects contains at least one object equivalent to another object.
/// Asserts that at least one element in the collection is equivalent to <paramref name="expectation"/>.
/// </summary>
/// <remarks>
/// Objects within the collection are equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another
/// and the result is equal.
/// <para>
/// <b>Important:</b> You cannot use this method to assert whether a subset of the collection is equivalent to the <paramref name="expectation"/>.
/// This usually means that the expectation is meant to be a <i>single</i> item.
/// </para>
/// <para>
/// By default, objects within the collection are seen as equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects.
/// Notice that actual behavior is determined by the global defaults managed by <see cref="AssertionOptions"/>.
/// </para>
/// </remarks>
/// <param name="expectation">The expected element.</param>
/// <param name="config">
Expand Down Expand Up @@ -875,7 +887,7 @@ public AndWhichConstraint<TAssertions, T> ContainEquivalentOf<TExpectation>(TExp
using var scope = new AssertionScope();
scope.AddReportable("configuration", () => options.ToString());

foreach (T actualItem in Subject)
foreach (T actualItem in Subject!)
{
var context =
new EquivalencyValidationContext(Node.From<TExpectation>(() => AssertionScope.Current.CallerIdentity),
Expand Down Expand Up @@ -1375,7 +1387,7 @@ public AndConstraint<TAssertions> HaveCount(int expected, string because = "", p

if (success)
{
int actualCount = Subject.Count();
int actualCount = Subject!.Count();

Execute.Assertion
.ForCondition(actualCount == expected)
Expand Down Expand Up @@ -1415,7 +1427,7 @@ public AndConstraint<TAssertions> HaveCount(Expression<Func<int, bool>> countPre
{
Func<int, bool> compiledPredicate = countPredicate.Compile();

int actualCount = Subject.Count();
int actualCount = Subject!.Count();

if (!compiledPredicate(actualCount))
{
Expand Down Expand Up @@ -1579,7 +1591,7 @@ public AndWhichConstraint<TAssertions, T> HaveElementAt(int index, T element, st

if (success)
{
if (index < Subject.Count())
if (index < Subject!.Count())
{
actual = Subject.ElementAt(index);

Expand Down Expand Up @@ -1731,7 +1743,7 @@ public AndConstraint<TAssertions> IntersectWith(IEnumerable<T> otherCollection,

if (success)
{
IEnumerable<T> sharedItems = Subject.Intersect(otherCollection);
IEnumerable<T> sharedItems = Subject!.Intersect(otherCollection);

Execute.Assertion
.BecauseOf(because, becauseArgs)
Expand Down Expand Up @@ -1844,7 +1856,7 @@ public AndConstraint<TAssertions> NotBeEquivalentTo<TExpectation>(IEnumerable<TE

using (var scope = new AssertionScope())
{
Subject.Should().BeEquivalentTo(unexpected, config);
BeEquivalentTo(unexpected, config);

failures = scope.Discard();
}
Expand Down Expand Up @@ -2225,7 +2237,7 @@ public AndConstraint<TAssertions> NotContain(Expression<Func<T, bool>> predicate
if (success)
{
Func<T, bool> compiledPredicate = predicate.Compile();
IEnumerable<T> unexpectedItems = Subject.Where(item => compiledPredicate(item));
IEnumerable<T> unexpectedItems = Subject!.Where(item => compiledPredicate(item));

Execute.Assertion
.BecauseOf(because, becauseArgs)
Expand Down Expand Up @@ -2267,7 +2279,7 @@ public AndConstraint<TAssertions> NotContain(IEnumerable<T> unexpected, string b

if (success)
{
IEnumerable<T> foundItems = unexpectedObjects.Intersect(Subject);
IEnumerable<T> foundItems = unexpectedObjects.Intersect(Subject!);

if (foundItems.Any())
{
Expand All @@ -2292,13 +2304,18 @@ public AndConstraint<TAssertions> NotContain(IEnumerable<T> unexpected, string b
}

/// <summary>
/// Asserts that a collection of objects does not contain any object equivalent to another object.
/// Asserts that no element in the collection is equivalent to <paramref name="unexpected"/>.
/// </summary>
/// <remarks>
/// Objects within the collection are equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another
/// and the result is equal.
/// <para>
/// <b>Important:</b> You cannot use this method to assert whether a subset of the collection is not equivalent to the <paramref name="unexpected"/>.
/// This usually means that the expectation is meant to be a <i>single</i> item.
/// </para>
/// <para>
/// By default, objects within the collection are seen as not equivalent to the expected object when both object graphs have unequally named properties with the same
/// value, irrespective of the type of those objects.
/// Notice that actual behavior is determined by the global defaults managed by <see cref="AssertionOptions"/>.
/// </para>
/// </remarks>
/// <param name="unexpected">The unexpected element.</param>
/// <param name="because">
Expand All @@ -2315,13 +2332,18 @@ public AndConstraint<TAssertions> NotContainEquivalentOf<TExpectation>(TExpectat
}

/// <summary>
/// Asserts that a collection of objects does not contain any object equivalent to another object.
/// Asserts that no element in the collection is equivalent to <paramref name="unexpected"/>.
/// </summary>
/// <remarks>
/// Objects within the collection are equivalent to the expected object when both object graphs have equally named properties with the same
/// value, irrespective of the type of those objects. Two properties are also equal if one type can be converted to another
/// and the result is equal.
/// <para>
/// <b>Important:</b> You cannot use this method to assert whether a subset of the collection is not equivalent to the <paramref name="unexpected"/>.
/// This usually means that the expectation is meant to be a <i>single</i> item.
/// </para>
/// <para>
/// By default, objects within the collection are seen as not equivalent to the expected object when both object graphs have unequally named properties with the same
/// value, irrespective of the type of those objects.
/// Notice that actual behavior is determined by the global defaults managed by <see cref="AssertionOptions"/>.
/// </para>
/// </remarks>
/// <param name="unexpected">The unexpected element.</param>
/// <param name="config">
Expand Down Expand Up @@ -2361,7 +2383,7 @@ public AndConstraint<TAssertions> NotContainEquivalentOf<TExpectation>(TExpectat
{
int index = 0;

foreach (T actualItem in Subject)
foreach (T actualItem in Subject!)
{
var context =
new EquivalencyValidationContext(Node.From<TExpectation>(() => AssertionScope.Current.CallerIdentity),
Expand Down Expand Up @@ -2598,7 +2620,7 @@ public AndConstraint<TAssertions> NotContainNulls<TKey>(Expression<Func<T, TKey>
{
Func<T, TKey> compiledPredicate = predicate.Compile();

T[] values = Subject
T[] values = Subject!
.Where(e => compiledPredicate(e) is null)
.ToArray();

Expand Down Expand Up @@ -2631,7 +2653,7 @@ public AndConstraint<TAssertions> NotContainNulls(string because = "", params ob

if (success)
{
int[] indices = Subject
int[] indices = Subject!
.Select((item, index) => (Item: item, Index: index))
.Where(e => e.Item is null)
.Select(e => e.Index)
Expand Down Expand Up @@ -2870,7 +2892,7 @@ public AndConstraint<TAssertions> OnlyHaveUniqueItems<TKey>(Expression<Func<T, T
{
Func<T, TKey> compiledPredicate = predicate.Compile();

IGrouping<TKey, T>[] groupWithMultipleItems = Subject
IGrouping<TKey, T>[] groupWithMultipleItems = Subject!
.GroupBy(compiledPredicate)
.Where(g => g.Count() > 1)
.ToArray();
Expand Down Expand Up @@ -2920,7 +2942,7 @@ public AndConstraint<TAssertions> OnlyHaveUniqueItems(string because = "", param

if (success)
{
IEnumerable<T> groupWithMultipleItems = Subject
IEnumerable<T> groupWithMultipleItems = Subject!
.GroupBy(o => o)
.Where(g => g.Count() > 1)
.Select(g => g.Key);
Expand Down
Loading
Loading