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

Release 6.7 #1927

Merged
merged 54 commits into from May 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
d53e8d9
Delete unused Guard to improve code coverage (#1871)
ITaluone Apr 5, 2022
5493e44
Fix codestyle issue `IDE0019`
ITaluone Mar 30, 2022
8d9a383
Fix codestyle issue `IDE0055`
ITaluone Mar 30, 2022
b456f5b
Update documentation for event monitoring at .netstandard2.0 (#1879)
ITaluone Apr 7, 2022
f5f65ea
Fix codestyle issue `IDE0052`
ITaluone Apr 6, 2022
a2fd3eb
Fix codestyle issue `SA1601`
ITaluone Apr 7, 2022
d6ad279
Merge pull request #1881 from ITaluone/code-style-fixes
jnyrup Apr 7, 2022
db4562b
Add missing tests (according to coveralls) for Data* objects (#1882)
IT-VBFK Apr 9, 2022
192f48c
Add `BeDefined` and `NotBeDefined` to `EnumAssertions` (#1888)
IT-VBFK Apr 15, 2022
9e03620
Fix small typo
IT-VBFK Apr 15, 2022
37c8e6b
Add guarding Should to range assertions
jnyrup Apr 15, 2022
9987ff3
Verify that guarding Equals method throws
jnyrup Apr 15, 2022
d5ef320
Add missing test for matching `null` with wildcard string
IT-VBFK Apr 15, 2022
471fb8e
Remove an unreachable `null` check
IT-VBFK Apr 16, 2022
6ecd349
Restore basic assertions for collections in System.Data (#1812)
logiclrd Apr 16, 2022
e87aa94
Add the ability to exclude non-browsable members from equivalency tes…
logiclrd Apr 16, 2022
94dd522
Merge pull request #1891 from jnyrup/FakeShould
jnyrup Apr 16, 2022
e9d81a8
Fix release notes
IT-VBFK Apr 16, 2022
b4bb85a
Seperate all DateTime assertions into nested classes
IT-VBFK Apr 18, 2022
7845bb3
Seperate all enum assertions into nested classes
IT-VBFK Apr 18, 2022
5699e93
Seperate all DateTimeOffset assertions into nested classes
IT-VBFK Apr 18, 2022
0b418c4
Merge pull request #1898 from IT-VBFK/structure-assertions-with-neste…
dennisdoomen Apr 18, 2022
02b5797
Add missing `null` check tests in Data*Specs
IT-VBFK Apr 18, 2022
ee621f9
Seperate all `Guid` assertions into nested classes
IT-VBFK Apr 18, 2022
b971ce9
Seperate all `object` assertions into nested classes
IT-VBFK Apr 18, 2022
cdbab6b
NUKE 6.0.1 -> 6.0.2
jnyrup Apr 18, 2022
ff23290
ReportGenerator 5.1.3 -> 5.1.4
jnyrup Apr 18, 2022
05706d4
GitVersion.Tool 5.9.0 -> 5.10.1
jnyrup Apr 18, 2022
6fecad6
JetBrains.Annotations 2021.3.0 -> 2022.1.0
jnyrup Apr 18, 2022
aa9e05e
Verify.Xunit 16.4.4 -> 16.5.4
jnyrup Apr 18, 2022
293caba
Bogus 34.0.1 -> 34.0.2
jnyrup Apr 18, 2022
b7d63c0
MSTest 2.2.8 -> 2.2.9
jnyrup Apr 18, 2022
0aa7417
Cleanup README.md (#1905)
robvanuden Apr 19, 2022
37a241b
Add `For`/`Exclude` to allow exclusion of members inside a collection…
whymatter Apr 20, 2022
d6b2d52
Seperate all `string` assertions into nested classes (#1903)
IT-VBFK Apr 20, 2022
09dc7fd
Update docxml comments on BeInRange
andrewlock Apr 21, 2022
3634163
Try to stabilize UIFact tests by running them sequentially
jnyrup Apr 18, 2022
17fedb1
Seperate all `Collection` assertions into nested classes
IT-VBFK Apr 19, 2022
083921a
Seperate all `Event` assertions into nested classes
IT-VBFK Apr 22, 2022
4e98b0d
Seperate all `Specialized` assertions into nested classes
IT-VBFK Apr 22, 2022
59fd88a
Seperate all `Stream` assertions into nested classes
IT-VBFK Apr 22, 2022
9cf8638
Merge pull request #1908 from IT-VBFK/structure-assertions-with-neste…
jnyrup Apr 23, 2022
68f41b9
Add overload to `HaveElement()` to be able to assert on occurrences f…
ITaluone Apr 23, 2022
28883ba
Seperate all `Xml` assertions into nested classes
IT-VBFK Apr 23, 2022
d96b587
Seperate all `Types` assertions into nested classes
IT-VBFK Apr 23, 2022
fb4edca
Merge pull request #1909 from IT-VBFK/structure-assertions-with-neste…
dennisdoomen Apr 24, 2022
d1e4318
Fix coveralls badge (#1906)
ITaluone Apr 25, 2022
ec0bdf4
Fix codestyle issue `IDE0055`
ITaluone Apr 25, 2022
717cfbb
Disable compiler warning for `SA1601` for test projects
ITaluone Apr 25, 2022
c98fb63
Move all tests from `CollectionAssertionSpecs` to the appropriate files
IT-VBFK Apr 24, 2022
29ab732
Rename CollectionAssertionSpecs.CommonAssertions.cs to CollectionAsse…
IT-VBFK Apr 25, 2022
c33006c
Fix the failure message for occurrence regex (#1913)
ITaluone Apr 27, 2022
463edb9
Do not add all arguments of type `T` to the matching events, if one i…
ITaluone May 18, 2022
06e21d8
Release 6.7
dennisdoomen May 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions Build/_build.csproj
Expand Up @@ -8,10 +8,10 @@
<NukeScriptDirectory>..\</NukeScriptDirectory>
</PropertyGroup>
<ItemGroup>
<PackageDownload Include="GitVersion.Tool" Version="[5.9.0]" />
<PackageDownload Include="GitVersion.Tool" Version="[5.10.1]" />
<PackageDownload Include="NSpec" Version="[3.1.0]" />
<PackageDownload Include="ReportGenerator" Version="[5.1.3]" />
<PackageDownload Include="ReportGenerator" Version="[5.1.4]" />
<PackageDownload Include="xunit.runner.console" Version="[2.4.1]" />
<PackageReference Include="Nuke.Common" Version="6.0.1" />
<PackageReference Include="Nuke.Common" Version="6.0.2" />
</ItemGroup>
</Project>
6 changes: 2 additions & 4 deletions README.md
Expand Up @@ -3,7 +3,7 @@
[![](https://img.shields.io/nuget/dt/FluentAssertions.svg?label=nuget%20downloads)](https://www.nuget.org/packages/FluentAssertions)
[![](https://img.shields.io/librariesio/dependents/nuget/FluentAssertions.svg?label=dependent%20libraries)](https://libraries.io/nuget/FluentAssertions)
![](https://img.shields.io/badge/release%20strategy-githubflow-orange.svg)
[![Coverage Status](https://coveralls.io/repos/github/fluentassertions/fluentassertions/badge.svg?branch=develop)](https://coveralls.io/github/fluentassertions/fluentassertions?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/fluentassertions/fluentassertions/badge.svg?branch=master)](https://coveralls.io/github/fluentassertions/fluentassertions?branch=master)

# About this project
A very extensive set of extension methods that allow you to more naturally specify the expected outcome of a TDD or BDD-style unit tests. Targets .NET Framework 4.7, as well as .NET Core 2.1, .NET Core 3.0, .NET 6, .NET Standard 2.0 and 2.1.
Expand All @@ -19,9 +19,7 @@ Install Visual Studio 2022 17.0+ or JetBrains Rider 2021.3 as well as the Build
# What are these Approval.Tests?
This is a special set of tests that use the [Verify](https://github.com/VerifyTests/Verify) project to verify whether you've introduced any breaking changes in the public API of the library.

If you've verified the changes and decided they are valid, you can accept them using `AcceptApiChanges.ps1` or `AcceptApiChanges.sh`. Alternatively, you can use the [Verify Support](https://plugins.jetbrains.com/plugin/17240-verify-support) plug-in to compare the changes and accept them right from inside Rider. See also the [Contribution Guidelines.](CONTRIBUTING.md).

`build.ps1`
If you've verified the changes and decided they are valid, you can accept them using `AcceptApiChanges.ps1` or `AcceptApiChanges.sh`. Alternatively, you can use the [Verify Support](https://plugins.jetbrains.com/plugin/17240-verify-support) plug-in to compare the changes and accept them right from inside Rider. See also the [Contribution Guidelines](CONTRIBUTING.md).

# Powered By
<a href="https://www.jetbrains.com/rider/"><img src="docs/assets/images/jetbrainsrider.svg" style="width:150px"/></a> <br/>and <br>
Expand Down
47 changes: 47 additions & 0 deletions Src/FluentAssertions/AssertionExtensions.cs
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Reflection;
Expand Down Expand Up @@ -385,6 +386,36 @@ public static StringCollectionAssertions Should(this IEnumerable<string> @this)
return new GenericDictionaryAssertions<TCollection, TKey, TValue>(actualValue);
}

/// <summary>
/// Returns an assertions object that provides methods for asserting the state of a <see cref="DataTableCollection"/>.
/// </summary>
[Pure]
public static GenericCollectionAssertions<DataTable> Should(this DataTableCollection actualValue)
{
return new GenericCollectionAssertions<DataTable>(
ReadOnlyNonGenericCollectionWrapper.Create(actualValue));
}

/// <summary>
/// Returns an assertions object that provides methods for asserting the state of a <see cref="DataColumnCollection"/>.
/// </summary>
[Pure]
public static GenericCollectionAssertions<DataColumn> Should(this DataColumnCollection actualValue)
{
return new GenericCollectionAssertions<DataColumn>(
ReadOnlyNonGenericCollectionWrapper.Create(actualValue));
}

/// <summary>
/// Returns an assertions object that provides methods for asserting the state of a <see cref="DataRowCollection"/>.
/// </summary>
[Pure]
public static GenericCollectionAssertions<DataRow> Should(this DataRowCollection actualValue)
{
return new GenericCollectionAssertions<DataRow>(
ReadOnlyNonGenericCollectionWrapper.Create(actualValue));
}

/// <summary>
/// Returns a <see cref="DataColumnAssertions"/> object that can be used to assert the
/// current <see cref="DataColumn"/>.
Expand Down Expand Up @@ -1028,6 +1059,22 @@ public static void Should(this TypeSelectorAssertions _)
InvalidShouldCall();
}

/// <inheritdoc cref="Should(ExecutionTimeAssertions)" />
[Obsolete("You are asserting the 'AndConstraint' itself. Remove the 'Should()' method directly following 'And'", error: true)]
public static void Should<TAssertions>(this DateTimeRangeAssertions<TAssertions> _)
where TAssertions : DateTimeAssertions<TAssertions>
{
InvalidShouldCall();
}

/// <inheritdoc cref="Should(ExecutionTimeAssertions)" />
[Obsolete("You are asserting the 'AndConstraint' itself. Remove the 'Should()' method directly following 'And'", error: true)]
public static void Should<TAssertions>(this DateTimeOffsetRangeAssertions<TAssertions> _)
where TAssertions : DateTimeOffsetAssertions<TAssertions>
{
InvalidShouldCall();
}

[DoesNotReturn]
private static void InvalidShouldCall()
{
Expand Down
8 changes: 0 additions & 8 deletions Src/FluentAssertions/Common/Guard.cs
Expand Up @@ -55,14 +55,6 @@ public static void ThrowIfArgumentContainsNull<T>(IEnumerable<T> values, string
}
}

public static void ThrowIfArgumentContainsNull<T>(IEnumerable<T> values, string paramName, string message)
{
if (values.Any(t => t is null))
{
throw new ArgumentNullException(paramName, message);
}
}

public static void ThrowIfArgumentIsEmpty<T>(IEnumerable<T> values, string paramName, string message)
{
if (!values.Any())
Expand Down
24 changes: 18 additions & 6 deletions Src/FluentAssertions/Common/MemberPath.cs
Expand Up @@ -17,6 +17,8 @@ internal class MemberPath

private string[] segments;

private static readonly MemberPathSegmentEqualityComparer MemberPathSegmentEqualityComparer = new();

public MemberPath(IMember member, string parentPath)
: this(member.ReflectedType, member.DeclaringType, parentPath.Combine(member.Name))
{
Expand Down Expand Up @@ -53,7 +55,7 @@ public bool IsSameAs(MemberPath candidate)
{
string[] candidateSegments = candidate.Segments;

return candidateSegments.SequenceEqual(Segments);
return candidateSegments.SequenceEqual(Segments, MemberPathSegmentEqualityComparer);
}

return false;
Expand All @@ -64,15 +66,22 @@ private bool IsParentOf(MemberPath candidate)
string[] candidateSegments = candidate.Segments;

return candidateSegments.Length > Segments.Length &&
candidateSegments.Take(Segments.Length).SequenceEqual(Segments);
candidateSegments.Take(Segments.Length).SequenceEqual(Segments, MemberPathSegmentEqualityComparer);
}

private bool IsChildOf(MemberPath candidate)
{
string[] candidateSegments = candidate.Segments;

return candidateSegments.Length < Segments.Length
&& candidateSegments.SequenceEqual(Segments.Take(candidateSegments.Length));
&& candidateSegments.SequenceEqual(Segments.Take(candidateSegments.Length),
MemberPathSegmentEqualityComparer);
}

public MemberPath AsParentCollectionOf(MemberPath nextPath)
{
var extendedDottedPath = dottedPath.Combine(nextPath.dottedPath, "[]");
return new MemberPath(declaringType, nextPath.reflectedType, extendedDottedPath);
}

/// <summary>
Expand All @@ -86,7 +95,7 @@ public bool IsEquivalentTo(string path)
public bool HasSameParentAs(MemberPath path)
{
return Segments.Length == path.Segments.Length
&& GetParentSegments().SequenceEqual(path.GetParentSegments());
&& GetParentSegments().SequenceEqual(path.GetParentSegments(), MemberPathSegmentEqualityComparer);
}

private IEnumerable<string> GetParentSegments() => Segments.Take(Segments.Length - 1);
Expand All @@ -96,6 +105,11 @@ public bool HasSameParentAs(MemberPath path)
/// </summary>
public bool GetContainsSpecificCollectionIndex() => dottedPath.ContainsSpecificCollectionIndex();

private string[] Segments =>
segments ??= dottedPath
.Replace("[]", "[*]", StringComparison.Ordinal)
.Split(new[] { '.', '[', ']' }, StringSplitOptions.RemoveEmptyEntries);

/// <summary>
/// Returns a copy of the current object as if it represented an un-indexed item in a collection.
/// </summary>
Expand All @@ -104,8 +118,6 @@ public MemberPath WithCollectionAsRoot()
return new MemberPath(reflectedType, declaringType, "[]." + dottedPath);
}

private string[] Segments => segments ??= dottedPath.Split(new[] { '.', '[', ']' }, StringSplitOptions.RemoveEmptyEntries);

/// <summary>
/// Returns the name of the member the current path points to without its parent path.
/// </summary>
Expand Down
49 changes: 49 additions & 0 deletions Src/FluentAssertions/Common/MemberPathSegmentEqualityComparer.cs
@@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace FluentAssertions.Common
{
/// <summary>
/// Compares two segments of a <see cref="MemberPath"/>.
/// Sets the <see cref="AnyIndexQualifier"/> equal with any numeric index qualifier.
/// All other comparisons are default string equality.
/// </summary>
internal class MemberPathSegmentEqualityComparer : IEqualityComparer<string>
{
private const string AnyIndexQualifier = "*";
private static readonly Regex IndexQualifierRegex = new(@"^\d+$");

/// <summary>
/// Compares two segments of a <see cref="MemberPath"/>.
/// </summary>
/// <param name="x">Left part of the comparison.</param>
/// <param name="y">Right part of the comparison.</param>
/// <returns>True if segments are equal, false if not.</returns>
public bool Equals(string x, string y)
{
if (x == AnyIndexQualifier)
{
return IsIndexQualifier(y);
}

if (y == AnyIndexQualifier)
{
return IsIndexQualifier(x);
}

return x == y;
}

private static bool IsIndexQualifier(string segment)
=> segment == AnyIndexQualifier || IndexQualifierRegex.IsMatch(segment);

public int GetHashCode(string obj)
{
#if NETCOREAPP2_1_OR_GREATER
return obj.GetHashCode(System.StringComparison.Ordinal);
#else
return obj.GetHashCode();
#endif
}
}
}
75 changes: 75 additions & 0 deletions Src/FluentAssertions/Common/ReadOnlyNonGenericCollectionWrapper.cs
@@ -0,0 +1,75 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace FluentAssertions.Common
{
internal static class ReadOnlyNonGenericCollectionWrapper
{
public static ReadOnlyNonGenericCollectionWrapper<DataTableCollection, DataTable> Create(DataTableCollection collection)
{
return
(collection != null)
? new ReadOnlyNonGenericCollectionWrapper<DataTableCollection, DataTable>(collection)
: null;
}

public static ReadOnlyNonGenericCollectionWrapper<DataColumnCollection, DataColumn> Create(DataColumnCollection collection)
{
return
(collection != null)
? new ReadOnlyNonGenericCollectionWrapper<DataColumnCollection, DataColumn>(collection)
: null;
}

public static ReadOnlyNonGenericCollectionWrapper<DataRowCollection, DataRow> Create(DataRowCollection collection)
{
return
(collection != null)
? new ReadOnlyNonGenericCollectionWrapper<DataRowCollection, DataRow>(collection)
: null;
}
}

internal class ReadOnlyNonGenericCollectionWrapper<TCollection, TItem> : ICollection<TItem>
where TCollection : ICollection
{
public TCollection UnderlyingCollection { get; }

public ReadOnlyNonGenericCollectionWrapper(TCollection collection)
{
Guard.ThrowIfArgumentIsNull(collection, nameof(collection));

UnderlyingCollection = collection;
}

public int Count => UnderlyingCollection.Count;

public bool IsReadOnly => true;

public IEnumerator<TItem> GetEnumerator() => UnderlyingCollection.Cast<TItem>().GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => UnderlyingCollection.GetEnumerator();

public bool Contains(TItem item) => UnderlyingCollection.Cast<TItem>().Contains(item);

public void CopyTo(TItem[] array, int arrayIndex) => UnderlyingCollection.CopyTo(array, arrayIndex);

/*

Mutation is not supported, but these methods must be implemented to satisfy ICollection<T>:
* Add
* Clear
* Remove

*/

public void Add(TItem item) => throw new NotSupportedException();

public void Clear() => throw new NotSupportedException();

public bool Remove(TItem item) => throw new NotSupportedException();
}
}