diff --git a/.editorconfig b/.editorconfig
index 985f244830c..7b22689bcf9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -227,4 +227,5 @@ dotnet_diagnostic.CA1200.severity = none # Avoid using cref tags with a prefix
dotnet_diagnostic.CA1707.severity = none # Remove the underscores from type name
dotnet_diagnostic.CA1720.severity = none # Identifier contains type name
dotnet_diagnostic.CA1810.severity = none # Do not use static constructors
+dotnet_diagnostic.CA1859.severity = none # Use concrete types when possible for improved performance
dotnet_diagnostic.CA2007.severity = none # Consider calling ConfigureAwait on the awaited task
diff --git a/AsyncCollectionAsserts.cs b/AsyncCollectionAsserts.cs
new file mode 100644
index 00000000000..a82ff214cc2
--- /dev/null
+++ b/AsyncCollectionAsserts.cs
@@ -0,0 +1,466 @@
+#if NETCOREAPP3_0_OR_GREATER
+
+#if XUNIT_NULLABLE
+#nullable enable
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xunit.Internal;
+using Xunit.Sdk;
+
+namespace Xunit
+{
+#if XUNIT_VISIBILITY_INTERNAL
+ internal
+#else
+ public
+#endif
+ partial class Assert
+ {
+ ///
+ /// Verifies that all items in the collection pass when executed against
+ /// action.
+ ///
+ /// The type of the object to be verified
+ /// The collection
+ /// The action to test each item against
+ /// Thrown when the collection contains at least one non-matching element
+ public static void All(
+ IAsyncEnumerable collection,
+ Action action) =>
+ All(AssertHelper.ToEnumerable(collection), action);
+
+ ///
+ /// Verifies that all items in the collection pass when executed against
+ /// action. The item index is provided to the action, in addition to the item.
+ ///
+ /// The type of the object to be verified
+ /// The collection
+ /// The action to test each item against
+ /// Thrown when the collection contains at least one non-matching element
+ public static void All(
+ IAsyncEnumerable collection,
+ Action action) =>
+ All(AssertHelper.ToEnumerable(collection), action);
+
+ ///
+ /// Verifies that all items in the collection pass when executed against
+ /// action.
+ ///
+ /// The type of the object to be verified
+ /// The collection
+ /// The action to test each item against
+ /// Thrown when the collection contains at least one non-matching element
+ public static Task AllAsync(
+ IAsyncEnumerable collection,
+ Func action) =>
+ AllAsync(AssertHelper.ToEnumerable(collection), action);
+
+ ///
+ /// Verifies that all items in the collection pass when executed against
+ /// action. The item index is provided to the action, in addition to the item.
+ ///
+ /// The type of the object to be verified
+ /// The collection
+ /// The action to test each item against
+ /// Thrown when the collection contains at least one non-matching element
+ public static Task AllAsync(
+ IAsyncEnumerable collection,
+ Func action) =>
+ AllAsync(AssertHelper.ToEnumerable(collection), action);
+
+ ///
+ /// Verifies that a collection contains exactly a given number of elements, which meet
+ /// the criteria provided by the element inspectors.
+ ///
+ /// The type of the object to be verified
+ /// The collection to be inspected
+ /// The element inspectors, which inspect each element in turn. The
+ /// total number of element inspectors must exactly match the number of elements in the collection.
+ public static void Collection(
+ IAsyncEnumerable collection,
+ params Action[] elementInspectors) =>
+ Collection(AssertHelper.ToEnumerable(collection), elementInspectors);
+
+ ///
+ /// Verifies that a collection contains exactly a given number of elements, which meet
+ /// the criteria provided by the element inspectors.
+ ///
+ /// The type of the object to be verified
+ /// The collection to be inspected
+ /// The element inspectors, which inspect each element in turn. The
+ /// total number of element inspectors must exactly match the number of elements in the collection.
+ public static Task CollectionAsync(
+ IAsyncEnumerable collection,
+ params Func[] elementInspectors) =>
+ CollectionAsync(AssertHelper.ToEnumerable(collection), elementInspectors);
+
+ ///
+ /// Verifies that a collection contains a given object.
+ ///
+ /// The type of the object to be verified
+ /// The object expected to be in the collection
+ /// The collection to be inspected
+ /// Thrown when the object is not present in the collection
+ public static void Contains(
+ T expected,
+ IAsyncEnumerable collection) =>
+ Contains(expected, AssertHelper.ToEnumerable(collection));
+
+ ///
+ /// Verifies that a collection contains a given object, using an equality comparer.
+ ///
+ /// The type of the object to be verified
+ /// The object expected to be in the collection
+ /// The collection to be inspected
+ /// The comparer used to equate objects in the collection with the expected object
+ /// Thrown when the object is not present in the collection
+ public static void Contains(
+ T expected,
+ IAsyncEnumerable collection,
+ IEqualityComparer comparer) =>
+ Contains(expected, AssertHelper.ToEnumerable(collection), comparer);
+
+ ///
+ /// Verifies that a collection contains a given object.
+ ///
+ /// The type of the object to be verified
+ /// The collection to be inspected
+ /// The filter used to find the item you're ensuring the collection contains
+ /// Thrown when the object is not present in the collection
+ public static void Contains(
+ IAsyncEnumerable collection,
+ Predicate filter) =>
+ Contains(AssertHelper.ToEnumerable(collection), filter);
+
+ ///
+ /// Verifies that a collection contains each object only once.
+ ///
+ /// The type of the object to be compared
+ /// The collection to be inspected
+ /// Thrown when an object is present inside the collection more than once
+ public static void Distinct(IAsyncEnumerable collection) =>
+ Distinct(AssertHelper.ToEnumerable(collection), EqualityComparer.Default);
+
+ ///
+ /// Verifies that a collection contains each object only once.
+ ///
+ /// The type of the object to be compared
+ /// The collection to be inspected
+ /// The comparer used to equate objects in the collection with the expected object
+ /// Thrown when an object is present inside the collection more than once
+ public static void Distinct(
+ IAsyncEnumerable collection,
+ IEqualityComparer comparer) =>
+ Distinct(AssertHelper.ToEnumerable(collection), comparer);
+
+ ///
+ /// Verifies that a collection does not contain a given object.
+ ///
+ /// The type of the object to be compared
+ /// The object that is expected not to be in the collection
+ /// The collection to be inspected
+ /// Thrown when the object is present inside the collection
+ public static void DoesNotContain(
+ T expected,
+ IAsyncEnumerable collection) =>
+ DoesNotContain(expected, AssertHelper.ToEnumerable(collection));
+
+ ///
+ /// Verifies that a collection does not contain a given object, using an equality comparer.
+ ///
+ /// The type of the object to be compared
+ /// The object that is expected not to be in the collection
+ /// The collection to be inspected
+ /// The comparer used to equate objects in the collection with the expected object
+ /// Thrown when the object is present inside the collection
+ public static void DoesNotContain(
+ T expected,
+ IAsyncEnumerable collection,
+ IEqualityComparer comparer) =>
+ DoesNotContain(expected, AssertHelper.ToEnumerable(collection), comparer);
+
+ ///
+ /// Verifies that a collection does not contain a given object.
+ ///
+ /// The type of the object to be compared
+ /// The collection to be inspected
+ /// The filter used to find the item you're ensuring the collection does not contain
+ /// Thrown when the object is present inside the collection
+ public static void DoesNotContain(
+ IAsyncEnumerable collection,
+ Predicate filter) =>
+ DoesNotContain(AssertHelper.ToEnumerable(collection), filter);
+
+ ///
+ /// Verifies that a collection is empty.
+ ///
+ /// The collection to be inspected
+ /// Thrown when the collection is null
+ /// Thrown when the collection is not empty
+ public static void Empty(IAsyncEnumerable collection) =>
+ Empty(AssertHelper.ToEnumerable(collection));
+
+ ///
+ /// Verifies that two sequences are equivalent, using a default comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// Thrown when the objects are not equal
+ public static void Equal(
+#if XUNIT_NULLABLE
+ IEnumerable? expected,
+ IAsyncEnumerable? actual) =>
+#else
+ IEnumerable expected,
+ IAsyncEnumerable actual) =>
+#endif
+ Equal(expected, AssertHelper.ToEnumerable(actual), GetEqualityComparer());
+
+ ///
+ /// Verifies that two sequences are equivalent, using a default comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// Thrown when the objects are not equal
+ public static void Equal(
+#if XUNIT_NULLABLE
+ IAsyncEnumerable? expected,
+ IAsyncEnumerable? actual) =>
+#else
+ IAsyncEnumerable expected,
+ IAsyncEnumerable actual) =>
+#endif
+ Equal(AssertHelper.ToEnumerable(expected), AssertHelper.ToEnumerable(actual), GetEqualityComparer());
+
+ ///
+ /// Verifies that two sequences are equivalent, using a custom equatable comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// The comparer used to compare the two objects
+ /// Thrown when the objects are not equal
+ public static void Equal(
+#if XUNIT_NULLABLE
+ IEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ IEqualityComparer comparer) =>
+ Equal(expected, AssertHelper.ToEnumerable(actual), GetEqualityComparer>(new AssertEqualityComparerAdapter(comparer)));
+
+ ///
+ /// Verifies that two sequences are equivalent, using a custom equatable comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// The comparer used to compare the two objects
+ /// Thrown when the objects are not equal
+ public static void Equal(
+#if XUNIT_NULLABLE
+ IAsyncEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IAsyncEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ IEqualityComparer comparer) =>
+ Equal(AssertHelper.ToEnumerable(expected), AssertHelper.ToEnumerable(actual), GetEqualityComparer>(new AssertEqualityComparerAdapter(comparer)));
+
+ ///
+ /// Verifies that two collections are equal, using a comparer function against
+ /// items in the two collections.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// The function to compare two items for equality
+ public static void Equal(
+#if XUNIT_NULLABLE
+ IEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ Func comparer) =>
+ Equal(expected, AssertHelper.ToEnumerable(actual), AssertEqualityComparer.FromComparer(comparer));
+
+ ///
+ /// Verifies that two collections are equal, using a comparer function against
+ /// items in the two collections.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// The function to compare two items for equality
+ public static void Equal(
+#if XUNIT_NULLABLE
+ IAsyncEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IAsyncEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ Func comparer) =>
+ Equal(AssertHelper.ToEnumerable(expected), AssertHelper.ToEnumerable(actual), AssertEqualityComparer.FromComparer(comparer));
+
+ ///
+ /// Verifies that a collection is not empty.
+ ///
+ /// The collection to be inspected
+ /// Thrown when a null collection is passed
+ /// Thrown when the collection is empty
+ public static void NotEmpty(IAsyncEnumerable collection) =>
+ NotEmpty(AssertHelper.ToEnumerable(collection));
+
+ ///
+ /// Verifies that two sequences are not equivalent, using a default comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected object
+ /// The actual object
+ /// Thrown when the objects are equal
+ public static void NotEqual(
+#if XUNIT_NULLABLE
+ IEnumerable? expected,
+ IAsyncEnumerable? actual) =>
+#else
+ IEnumerable expected,
+ IAsyncEnumerable actual) =>
+#endif
+ NotEqual(expected, AssertHelper.ToEnumerable(actual), GetEqualityComparer());
+
+ ///
+ /// Verifies that two sequences are not equivalent, using a default comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected object
+ /// The actual object
+ /// Thrown when the objects are equal
+ public static void NotEqual(
+#if XUNIT_NULLABLE
+ IAsyncEnumerable? expected,
+ IAsyncEnumerable? actual) =>
+#else
+ IAsyncEnumerable expected,
+ IAsyncEnumerable actual) =>
+#endif
+ NotEqual(AssertHelper.ToEnumerable(expected), AssertHelper.ToEnumerable(actual), GetEqualityComparer());
+
+ ///
+ /// Verifies that two sequences are not equivalent, using a custom equality comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected object
+ /// The actual object
+ /// The comparer used to compare the two objects
+ /// Thrown when the objects are equal
+ public static void NotEqual(
+#if XUNIT_NULLABLE
+ IEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ IEqualityComparer comparer) =>
+ NotEqual(expected, AssertHelper.ToEnumerable(actual), GetEqualityComparer>(new AssertEqualityComparerAdapter(comparer)));
+
+ ///
+ /// Verifies that two sequences are not equivalent, using a custom equality comparer.
+ ///
+ /// The type of the objects to be compared
+ /// The expected object
+ /// The actual object
+ /// The comparer used to compare the two objects
+ /// Thrown when the objects are equal
+ public static void NotEqual(
+#if XUNIT_NULLABLE
+ IAsyncEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IAsyncEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ IEqualityComparer comparer) =>
+ NotEqual(AssertHelper.ToEnumerable(expected), AssertHelper.ToEnumerable(actual), GetEqualityComparer>(new AssertEqualityComparerAdapter(comparer)));
+
+ ///
+ /// Verifies that two collections are not equal, using a comparer function against
+ /// items in the two collections.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// The function to compare two items for equality
+ public static void NotEqual(
+#if XUNIT_NULLABLE
+ IEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ Func comparer) =>
+ NotEqual(expected, AssertHelper.ToEnumerable(actual), AssertEqualityComparer.FromComparer(comparer));
+
+ ///
+ /// Verifies that two collections are not equal, using a comparer function against
+ /// items in the two collections.
+ ///
+ /// The type of the objects to be compared
+ /// The expected value
+ /// The value to be compared against
+ /// The function to compare two items for equality
+ public static void NotEqual(
+#if XUNIT_NULLABLE
+ IAsyncEnumerable? expected,
+ IAsyncEnumerable? actual,
+#else
+ IAsyncEnumerable expected,
+ IAsyncEnumerable actual,
+#endif
+ Func comparer) =>
+ NotEqual(AssertHelper.ToEnumerable(expected), AssertHelper.ToEnumerable(actual), AssertEqualityComparer.FromComparer(comparer));
+
+ ///
+ /// Verifies that the given collection contains only a single
+ /// element of the given type.
+ ///
+ /// The collection type.
+ /// The collection.
+ /// The single item in the collection.
+ /// Thrown when the collection does not contain
+ /// exactly one element.
+ public static T Single(IAsyncEnumerable collection) =>
+ Single(AssertHelper.ToEnumerable(collection));
+
+ ///
+ /// Verifies that the given collection contains only a single
+ /// element of the given type which matches the given predicate. The
+ /// collection may or may not contain other values which do not
+ /// match the given predicate.
+ ///
+ /// The collection type.
+ /// The collection.
+ /// The item matching predicate.
+ /// The single item in the filtered collection.
+ /// Thrown when the filtered collection does
+ /// not contain exactly one element.
+ public static T Single(
+ IAsyncEnumerable collection,
+ Predicate predicate) =>
+ Single(AssertHelper.ToEnumerable(collection), predicate);
+ }
+}
+
+#endif
diff --git a/CollectionAsserts.cs b/CollectionAsserts.cs
index 96678fcfaec..d176724fb5f 100644
--- a/CollectionAsserts.cs
+++ b/CollectionAsserts.cs
@@ -226,13 +226,25 @@ partial class Assert
{
GuardArgumentNotNull(nameof(collection), collection);
- // We special case HashSet because it has a custom Contains implementation that is based on the comparer
- // passed into their constructors, which we don't have access to.
- var hashSet = collection as HashSet;
- if (hashSet != null)
- Contains(expected, hashSet);
- else
- Contains(expected, collection, GetEqualityComparer());
+ // We special case sets because they are constructed with their comparers, which we don't have access to.
+ // We want to let them do their normal logic when appropriate, and not try to use our default comparer.
+ var set = collection as ISet;
+ if (set != null)
+ {
+ Contains(expected, set);
+ return;
+ }
+#if NET5_0_OR_GREATER
+ var readOnlySet = collection as IReadOnlySet;
+ if (readOnlySet != null)
+ {
+ Contains(expected, readOnlySet);
+ return;
+ }
+#endif
+
+ // Fall back to the assumption that this is a linear container and use our default comparer
+ Contains(expected, collection, GetEqualityComparer());
}
///
@@ -326,13 +338,25 @@ partial class Assert
{
GuardArgumentNotNull(nameof(collection), collection);
- // We special case HashSet because it has a custom Contains implementation that is based on the comparer
- // passed into their constructors, which we don't have access to.
- var hashSet = collection as HashSet;
- if (hashSet != null)
- DoesNotContain(expected, hashSet);
- else
- DoesNotContain(expected, collection, GetEqualityComparer());
+ // We special case sets because they are constructed with their comparers, which we don't have access to.
+ // We want to let them do their normal logic when appropriate, and not try to use our default comparer.
+ var set = collection as ISet;
+ if (set != null)
+ {
+ DoesNotContain(expected, set);
+ return;
+ }
+#if NET5_0_OR_GREATER
+ var readOnlySet = collection as IReadOnlySet;
+ if (readOnlySet != null)
+ {
+ DoesNotContain(expected, readOnlySet);
+ return;
+ }
+#endif
+
+ // Fall back to the assumption that this is a linear container and use our default comparer
+ DoesNotContain(expected, collection, GetEqualityComparer());
}
///
diff --git a/Comparers.cs b/Comparers.cs
index 147b3cf7e7f..f4761545b9f 100644
--- a/Comparers.cs
+++ b/Comparers.cs
@@ -19,10 +19,6 @@ namespace Xunit
#endif
partial class Assert
{
- static IComparer GetComparer()
- where T : IComparable =>
- new AssertComparer();
-
#if XUNIT_NULLABLE
static IEqualityComparer GetEqualityComparer(IEqualityComparer? innerComparer = null) =>
new AssertEqualityComparer(innerComparer);
@@ -30,5 +26,9 @@ static IComparer GetComparer()
static IEqualityComparer GetEqualityComparer(IEqualityComparer innerComparer = null) =>
new AssertEqualityComparer(innerComparer);
#endif
+
+ static IComparer GetRangeComparer()
+ where T : IComparable =>
+ new AssertRangeComparer();
}
}
diff --git a/EqualityAsserts.cs b/EqualityAsserts.cs
index a3198d511aa..cd676e85e28 100644
--- a/EqualityAsserts.cs
+++ b/EqualityAsserts.cs
@@ -12,6 +12,7 @@
using System.Globalization;
using System.Linq;
using System.Reflection;
+using Xunit.Internal;
using Xunit.Sdk;
#if XUNIT_NULLABLE
@@ -176,7 +177,15 @@ partial class Assert
{
try
{
- if (CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer.DefaultInnerComparer, out mismatchedIndex))
+ bool result;
+
+ // Call AssertEqualityComparer.Equals because it checks for IEquatable<> before using CollectionTracker
+ if (aec != null)
+ result = aec.Equals(expected, expectedTracker, actual, actualTracker, out mismatchedIndex);
+ else
+ result = CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer.DefaultInnerComparer, out mismatchedIndex);
+
+ if (result)
return;
}
catch (Exception ex)
@@ -251,8 +260,8 @@ partial class Assert
if (expectedType != actualType)
{
- var expectedTypeName = expectedType == null ? "" : ArgumentFormatter.FormatTypeName(expectedType) + " ";
- var actualTypeName = actualType == null ? "" : ArgumentFormatter.FormatTypeName(actualType) + " ";
+ var expectedTypeName = expectedType == null ? "" : (AssertHelper.IsCompilerGenerated(expectedType) ? " " : ArgumentFormatter.FormatTypeName(expectedType) + " ");
+ var actualTypeName = actualType == null ? "" : (AssertHelper.IsCompilerGenerated(actualType) ? " " : ArgumentFormatter.FormatTypeName(actualType) + " ");
var typeNameIndent = Math.Max(expectedTypeName.Length, actualTypeName.Length);
@@ -651,7 +660,15 @@ partial class Assert
{
try
{
- if (!CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer.DefaultInnerComparer, out mismatchedIndex))
+ bool result;
+
+ // Call AssertEqualityComparer.Equals because it checks for IEquatable<> before using CollectionTracker
+ if (aec != null)
+ result = aec.Equals(expected, expectedTracker, actual, actualTracker, out mismatchedIndex);
+ else
+ result = CollectionTracker.AreCollectionsEqual(expectedTracker, actualTracker, itemComparer, itemComparer == AssertEqualityComparer.DefaultInnerComparer, out mismatchedIndex);
+
+ if (!result)
return;
// For NotEqual that doesn't throw, pointers are irrelevant, because
@@ -719,8 +736,8 @@ partial class Assert
if (expectedType != actualType)
{
- var expectedTypeName = expectedType == null ? "" : ArgumentFormatter.FormatTypeName(expectedType) + " ";
- var actualTypeName = actualType == null ? "" : ArgumentFormatter.FormatTypeName(actualType) + " ";
+ var expectedTypeName = expectedType == null ? "" : (AssertHelper.IsCompilerGenerated(expectedType) ? " " : ArgumentFormatter.FormatTypeName(expectedType) + " ");
+ var actualTypeName = actualType == null ? "" : (AssertHelper.IsCompilerGenerated(actualType) ? " " : ArgumentFormatter.FormatTypeName(actualType) + " ");
var typeNameIndent = Math.Max(expectedTypeName.Length, actualTypeName.Length);
diff --git a/README.md b/README.md
index ab204441bdc..721110887ee 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# About This Project
-This project contains the xUnit.net assertion library source code, intended to be used as a Git submodule. Code here is built with a target-framework of `netstandard1.1`, and must support both `net452` and `netcoreapp1.0`. The code must be buildable by a minimum of C# 6.0. These constraints are supported by the [suggested contribution workflow](#suggested-contribution-workflow), which makes it trivial to know when you've used unavailable features.
+This project contains the xUnit.net assertion library source code, intended to be used as a Git submodule.
+
+Code here is built with several target frameworks: `netstandard1.1` and `net6.0` for xUnit.net v2; and `netstandard2.0` and `net6.0` for xUnit.net v3. At a minimum the code needs to be able to support `net452` and later for .NET Framework, `netcoreapp1.0` and later for .NET Core, and `net5.0` and later for .NET. The minimum (and default) C# version is 6.0, unless specific features require targeting later compilers. Additionally, we compile with the full Roslyn analyzer set enabled when building for v3, so you will frequently see conditional code and/or rules being disabled as appropriate. These constraints are supported by the [suggested contribution workflow](#suggested-contribution-workflow), which aims to make it easy to know when you've used unavailable features.
> _**Note:** If your PR requires a newer target framework or a newer C# language to build, please start a discussion in the related issue(s) before starting any work. PRs that arbitrarily use newer target frameworks and/or newer C# language features will need to be fixed; you may be asked to fix them, or we may fix them for you, or we may decline the PR (at our discretion)._
diff --git a/RangeAsserts.cs b/RangeAsserts.cs
index 7686978dc2b..bddfdd87f56 100644
--- a/RangeAsserts.cs
+++ b/RangeAsserts.cs
@@ -31,7 +31,7 @@ partial class Assert
T low,
T high)
where T : IComparable =>
- InRange(actual, low, high, GetComparer());
+ InRange(actual, low, high, GetRangeComparer());
///
/// Verifies that a value is within a given range, using a comparer.
@@ -70,7 +70,7 @@ partial class Assert
T low,
T high)
where T : IComparable =>
- NotInRange(actual, low, high, GetComparer());
+ NotInRange(actual, low, high, GetRangeComparer());
///
/// Verifies that a value is not within a given range, using a comparer.
diff --git a/Sdk/ArgumentFormatter.cs b/Sdk/ArgumentFormatter.cs
index 69928bb93ec..9ad3b22faac 100644
--- a/Sdk/ArgumentFormatter.cs
+++ b/Sdk/ArgumentFormatter.cs
@@ -123,8 +123,12 @@ static ArgumentFormatter()
/// The string value to be escaped
public static string EscapeString(string s)
{
+#if NET6_0_OR_GREATER
+ ArgumentNullException.ThrowIfNull(s);
+#else
if (s == null)
throw new ArgumentNullException(nameof(s));
+#endif
var builder = new StringBuilder(s.Length);
for (var i = 0; i < s.Length; i++)
@@ -322,7 +326,11 @@ static string FormatCharValue(char value)
string.Format(CultureInfo.CurrentCulture, "{0:G17}", value);
static string FormatEnumValue(object value) =>
+#if NETCOREAPP2_0_OR_GREATER
+ value.ToString()?.Replace(", ", " | ", StringComparison.Ordinal) ?? "null";
+#else
value.ToString()?.Replace(", ", " | ") ?? "null";
+#endif
static string FormatEnumerableValue(
IEnumerable enumerable,
@@ -365,7 +373,11 @@ static string FormatCharValue(char value)
static string FormatStringValue(string value)
{
+#if NETCOREAPP2_0_OR_GREATER
+ value = EscapeString(value).Replace(@"""", @"\""", StringComparison.Ordinal); // escape double quotes
+#else
value = EscapeString(value).Replace(@"""", @"\"""); // escape double quotes
+#endif
if (value.Length > MAX_STRING_LENGTH)
{
@@ -452,7 +464,11 @@ static string FormatStringValue(string value)
if (result == null)
return typeInfo.Name;
+#if NETCOREAPP2_1_OR_GREATER
+ var tickIdx = result.IndexOf('`', StringComparison.Ordinal);
+#else
var tickIdx = result.IndexOf('`');
+#endif
if (tickIdx > 0)
result = result.Substring(0, tickIdx);
@@ -508,7 +524,11 @@ static bool IsAnonymousType(this TypeInfo typeInfo)
if (typeInfo.GetCustomAttribute(typeof(CompilerGeneratedAttribute)) == null)
return false;
+#if NETCOREAPP2_1_OR_GREATER
+ return typeInfo.Name.Contains("AnonymousType", StringComparison.Ordinal);
+#else
return typeInfo.Name.Contains("AnonymousType");
+#endif
}
static bool IsSZArrayType(this TypeInfo typeInfo)
@@ -565,8 +585,15 @@ static bool IsSZArrayType(this TypeInfo typeInfo)
return value != null;
}
- static Exception UnwrapException(Exception ex)
+#if XUNIT_NULLABLE
+ internal static Exception? UnwrapException(Exception? ex)
+#else
+ internal static Exception UnwrapException(Exception ex)
+#endif
{
+ if (ex == null)
+ return null;
+
while (true)
{
var tiex = ex as TargetInvocationException;
diff --git a/Sdk/AssertEqualityComparer.cs b/Sdk/AssertEqualityComparer.cs
index f1a3ee5ac57..ad627fe4982 100644
--- a/Sdk/AssertEqualityComparer.cs
+++ b/Sdk/AssertEqualityComparer.cs
@@ -15,6 +15,8 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
using System.Reflection;
#if XUNIT_NULLABLE
@@ -23,6 +25,84 @@
namespace Xunit.Sdk
{
+ static class AssertEqualityComparer
+ {
+ static readonly ConcurrentDictionary cachedDefaultComparers = new ConcurrentDictionary();
+ static readonly ConcurrentDictionary cachedDefaultInnerComparers = new ConcurrentDictionary();
+#if XUNIT_NULLABLE
+ static readonly object?[] singleNullObject = new object?[] { null };
+#else
+ static readonly object[] singleNullObject = new object[] { null };
+#endif
+
+ ///
+ /// Gets the default comparer to be used for the provided when a custom one
+ /// has not been provided. Creates an instance of wrapped
+ /// by .
+ ///
+ /// The type to be compared
+ internal static IEqualityComparer GetDefaultComparer(Type type) =>
+ cachedDefaultComparers.GetOrAdd(type, itemType =>
+ {
+ var comparerType = typeof(AssertEqualityComparer<>).MakeGenericType(itemType);
+ var comparer = Activator.CreateInstance(comparerType, singleNullObject);
+ if (comparer == null)
+ throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not create instance of AssertEqualityComparer<{0}>", itemType.FullName ?? itemType.Name));
+
+ var wrapperType = typeof(AssertEqualityComparerAdapter<>).MakeGenericType(itemType);
+ var result = Activator.CreateInstance(wrapperType, new object[] { comparer }) as IEqualityComparer;
+ if (result == null)
+ throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Could not create instance of AssertEqualityComparerAdapter<{0}>", itemType.FullName ?? itemType.Name));
+
+ return result;
+ });
+
+ ///
+ /// Gets the default comparer to be used as an inner comparer for the provided
+ /// when a custom one has not been provided. For non-collections, this defaults to an -based
+ /// comparer; for collections, this creates an inner comparer based on the item type in the collection.
+ ///
+ /// The type to create an inner comparer for
+ internal static IEqualityComparer GetDefaultInnerComparer(Type type) =>
+ cachedDefaultInnerComparers.GetOrAdd(type, t =>
+ {
+ var innerType = typeof(object);
+
+ // string is enumerable, but we don't treat it like a collection
+ if (t != typeof(string))
+ {
+ var enumerableOfT =
+ t.GetTypeInfo()
+ .ImplementedInterfaces
+ .Select(i => i.GetTypeInfo())
+ .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
+
+ if (enumerableOfT != null)
+ innerType = enumerableOfT.GenericTypeArguments[0];
+ }
+
+ return GetDefaultComparer(innerType);
+ });
+
+ ///
+ /// This exception is thrown when an operation failure has occured during equality comparison operations.
+ /// This generally indicates that a necessary pre-condition was not met for comparison operations to succeed.
+ ///
+ public sealed class OperationalFailureException : Exception
+ {
+ OperationalFailureException(string message) :
+ base(message)
+ { }
+
+ ///
+ /// Gets an exception that indicates that GetHashCode was called on
+ /// which usually indicates that an item comparison function was used to try to compare two hash sets.
+ ///
+ public static OperationalFailureException ForIllegalGetHashCode() =>
+ new OperationalFailureException("During comparison of two collections, GetHashCode was called, but only a comparison function was provided. This typically indicates trying to compare two sets with an item comparison function, which is not supported. For more information, see https://xunit.net/docs/hash-sets-vs-linear-containers");
+ }
+ }
+
///
/// Default implementation of used by the xUnit.net equality assertions
/// (except for collections, which are handled directly by the appropriate assertion methods).
@@ -30,7 +110,7 @@ namespace Xunit.Sdk
/// The type that is being compared.
sealed class AssertEqualityComparer : IEqualityComparer
{
- internal static readonly IEqualityComparer DefaultInnerComparer = new AssertEqualityComparerAdapter
/// The type that is being compared.
- sealed class AssertComparer : IComparer
+ sealed class AssertRangeComparer : IComparer
where T : IComparable
{
///
diff --git a/Sdk/CollectionTracker.cs b/Sdk/CollectionTracker.cs
index 4384ff44ed2..1c8cd475eca 100644
--- a/Sdk/CollectionTracker.cs
+++ b/Sdk/CollectionTracker.cs
@@ -40,8 +40,12 @@ abstract class CollectionTracker : IDisposable
///
protected CollectionTracker(IEnumerable innerEnumerable)
{
+#if NET6_0_OR_GREATER
+ ArgumentNullException.ThrowIfNull(innerEnumerable);
+#else
if (innerEnumerable == null)
throw new ArgumentNullException(nameof(innerEnumerable));
+#endif
InnerEnumerable = innerEnumerable;
}
@@ -86,7 +90,7 @@ protected CollectionTracker(IEnumerable innerEnumerable)
mismatchedIndex = null;
return
- CheckIfDictionariesAreEqual(x, y, itemComparer) ??
+ CheckIfDictionariesAreEqual(x, y) ??
CheckIfSetsAreEqual(x, y, isDefaultItemComparer ? null : itemComparer) ??
CheckIfArraysAreEqual(x, y, itemComparer, isDefaultItemComparer, out mismatchedIndex) ??
CheckIfEnumerablesAreEqual(x, y, itemComparer, isDefaultItemComparer, out mismatchedIndex);
@@ -150,12 +154,11 @@ protected CollectionTracker(IEnumerable innerEnumerable)
static bool? CheckIfDictionariesAreEqual(
#if XUNIT_NULLABLE
CollectionTracker? x,
- CollectionTracker? y,
+ CollectionTracker? y)
#else
CollectionTracker x,
- CollectionTracker y,
+ CollectionTracker y)
#endif
- IEqualityComparer itemComparer)
{
if (x == null || y == null)
return null;
@@ -171,6 +174,9 @@ protected CollectionTracker(IEnumerable innerEnumerable)
var dictionaryYKeys = new HashSet(dictionaryY.Keys.Cast());
+ // We don't pass along the itemComparer from AreCollectionsEqual because we aren't directly
+ // comparing the KeyValuePair<> objects. Instead we rely on Contains() on the dictionary to
+ // match up keys, and then create type-appropriate comparers for the values.
foreach (var key in dictionaryX.Keys.Cast())
{
if (!dictionaryYKeys.Contains(key))
@@ -179,8 +185,22 @@ protected CollectionTracker(IEnumerable innerEnumerable)
var valueX = dictionaryX[key];
var valueY = dictionaryY[key];
- if (!itemComparer.Equals(valueX, valueY))
+ if (valueX == null)
+ {
+ if (valueY != null)
+ return false;
+ }
+ else if (valueY == null)
return false;
+ else
+ {
+ var valueXType = valueX.GetType();
+ var valueYType = valueY.GetType();
+
+ var comparer = AssertEqualityComparer.GetDefaultComparer(valueXType == valueYType ? valueXType : typeof(object));
+ if (!comparer.Equals(valueX, valueY))
+ return false;
+ }
dictionaryYKeys.Remove(key);
}
@@ -416,8 +436,12 @@ sealed class CollectionTracker : CollectionTracker, IEnumerable
IEnumerable castCollection) :
base(collection)
{
+#if NET6_0_OR_GREATER
+ ArgumentNullException.ThrowIfNull(castCollection);
+#else
if (castCollection == null)
throw new ArgumentNullException(nameof(castCollection));
+#endif
this.collection = castCollection;
}
diff --git a/Sdk/Exceptions/AllException.cs b/Sdk/Exceptions/AllException.cs
index 2bd1faa91d5..ae707a8fbfc 100644
--- a/Sdk/Exceptions/AllException.cs
+++ b/Sdk/Exceptions/AllException.cs
@@ -58,10 +58,18 @@ partial class AllException : XunitException
CultureInfo.CurrentCulture,
"{0}Item: {1}{2}{3}Error: {4}",
string.Format(CultureInfo.CurrentCulture, "[{0}]:", error.Item1).PadRight(maxItemIndexLength),
+#if NETCOREAPP2_0_OR_GREATER
+ error.Item2.Replace(Environment.NewLine, wrapSpaces, StringComparison.Ordinal),
+#else
error.Item2.Replace(Environment.NewLine, wrapSpaces),
+#endif
Environment.NewLine,
indexSpaces,
+#if NETCOREAPP2_0_OR_GREATER
+ error.Item3.Message.Replace(Environment.NewLine, wrapSpaces, StringComparison.Ordinal)
+#else
error.Item3.Message.Replace(Environment.NewLine, wrapSpaces)
+#endif
)
)
)
diff --git a/Sdk/Exceptions/CollectionException.cs b/Sdk/Exceptions/CollectionException.cs
index 01903912dba..eb55146260a 100644
--- a/Sdk/Exceptions/CollectionException.cs
+++ b/Sdk/Exceptions/CollectionException.cs
@@ -1,10 +1,14 @@
#if XUNIT_NULLABLE
#nullable enable
+#else
+// In case this is source-imported with global nullable enabled but no XUNIT_NULLABLE
+#pragma warning disable CS8604
#endif
using System;
using System.Globalization;
using System.Linq;
+using Xunit.Internal;
namespace Xunit.Sdk
{
@@ -18,16 +22,27 @@ namespace Xunit.Sdk
#endif
partial class CollectionException : XunitException
{
+ static readonly char[] crlfSeparators = new[] { '\r', '\n' };
+
CollectionException(string message) :
base(message)
{ }
static string FormatInnerException(Exception innerException)
{
+ var text = innerException.Message;
+ var filteredStack = ExceptionUtility.TransformStackTrace(ExceptionUtility.FilterStackTrace(innerException.StackTrace), " ");
+ if (!string.IsNullOrWhiteSpace(filteredStack))
+ {
+ if (text.Length != 0)
+ text += Environment.NewLine;
+
+ text += "Stack Trace:" + Environment.NewLine + filteredStack;
+ }
+
var lines =
- innerException
- .Message
- .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
+ text
+ .Split(crlfSeparators, StringSplitOptions.RemoveEmptyEntries)
.Select((value, idx) => idx > 0 ? " " + value : value);
return string.Join(Environment.NewLine, lines);
diff --git a/Sdk/Exceptions/EqualException.cs b/Sdk/Exceptions/EqualException.cs
index d47eb305ac0..8a94e09c370 100644
--- a/Sdk/Exceptions/EqualException.cs
+++ b/Sdk/Exceptions/EqualException.cs
@@ -102,6 +102,10 @@ partial class EqualException : XunitException
{
Assert.GuardArgumentNotNull(nameof(actual), actual);
+ error = ArgumentFormatter.UnwrapException(error);
+ if (error is AssertEqualityComparer.OperationalFailureException)
+ return new EqualException("Assert.Equal() Failure: " + error.Message);
+
var message =
error == null
? string.Format(CultureInfo.CurrentCulture, "Assert.Equal() Failure: {0} differ", collectionDisplay ?? "Collections")
@@ -226,9 +230,17 @@ partial class EqualException : XunitException
"{0}{1}Expected: {2}{3}Actual: {4}",
message,
Environment.NewLine,
+#if NETCOREAPP2_0_OR_GREATER
+ expectedText.Replace(Environment.NewLine, newLineAndIndent, StringComparison.Ordinal),
+#else
expectedText.Replace(Environment.NewLine, newLineAndIndent),
+#endif
Environment.NewLine,
+#if NETCOREAPP2_0_OR_GREATER
+ actualText.Replace(Environment.NewLine, newLineAndIndent, StringComparison.Ordinal)
+#else
actualText.Replace(Environment.NewLine, newLineAndIndent)
+#endif
),
error
);
diff --git a/Sdk/Exceptions/ExceptionUtility.cs b/Sdk/Exceptions/ExceptionUtility.cs
new file mode 100644
index 00000000000..d0ed65a00a3
--- /dev/null
+++ b/Sdk/Exceptions/ExceptionUtility.cs
@@ -0,0 +1,99 @@
+#if XUNIT_NULLABLE
+#nullable enable
+#else
+// In case this is source-imported with global nullable enabled but no XUNIT_NULLABLE
+#pragma warning disable CS8603
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Xunit.Internal
+{
+ // Adapted from ExceptionUtility (xunit.v3.common) and StackFrameTransformer (xunit.v3.runner.common)
+ internal static class ExceptionUtility
+ {
+ static readonly Regex transformRegex;
+
+ static ExceptionUtility()
+ {
+ transformRegex = new Regex(@"^\s*at (?.*) in (?.*):(line )?(?\d+)$");
+ }
+
+ static bool FilterStackFrame(string stackFrame)
+ {
+ Assert.GuardArgumentNotNull(nameof(stackFrame), stackFrame);
+
+#if DEBUG
+ return false;
+#else
+ return stackFrame.StartsWith("at Xunit.", StringComparison.Ordinal);
+#endif
+ }
+
+#if XUNIT_NULLABLE
+ public static string? FilterStackTrace(string? stack)
+#else
+ public static string FilterStackTrace(string stack)
+#endif
+ {
+ if (stack == null)
+ return null;
+
+ var results = new List();
+
+ foreach (var line in stack.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
+ {
+ var trimmedLine = line.TrimStart();
+ if (!FilterStackFrame(trimmedLine))
+ results.Add(line);
+ }
+
+ return string.Join(Environment.NewLine, results.ToArray());
+ }
+
+#if XUNIT_NULLABLE
+ public static string? TransformStackFrame(
+ string? stackFrame,
+#else
+ public static string TransformStackFrame(
+ string stackFrame,
+#endif
+ string indent = "")
+ {
+ if (stackFrame == null)
+ return null;
+
+ var match = transformRegex.Match(stackFrame);
+ if (match == Match.Empty)
+ return stackFrame;
+
+ var file = match.Groups["file"].Value;
+ return string.Format(CultureInfo.InvariantCulture, "{0}{1}({2},0): at {3}", indent, file, match.Groups["line"].Value, match.Groups["method"].Value);
+ }
+
+#if XUNIT_NULLABLE
+ public static string? TransformStackTrace(
+ string? stack,
+#else
+ public static string TransformStackTrace(
+ string stack,
+#endif
+ string indent = "")
+ {
+ if (stack == null)
+ return null;
+
+ return string.Join(
+ Environment.NewLine,
+ stack
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.None)
+ .Select(frame => TransformStackFrame(frame, indent))
+ .ToArray()
+ );
+ }
+ }
+}
diff --git a/Sdk/Exceptions/NotEqualException.cs b/Sdk/Exceptions/NotEqualException.cs
index 04030cd8393..142c53a18e7 100644
--- a/Sdk/Exceptions/NotEqualException.cs
+++ b/Sdk/Exceptions/NotEqualException.cs
@@ -79,6 +79,10 @@ partial class NotEqualException : XunitException
Assert.GuardArgumentNotNull(nameof(expected), expected);
Assert.GuardArgumentNotNull(nameof(actual), actual);
+ error = ArgumentFormatter.UnwrapException(error);
+ if (error is AssertEqualityComparer.OperationalFailureException)
+ return new NotEqualException("Assert.NotEqual() Failure: " + error.Message);
+
var message =
error == null
? string.Format(CultureInfo.CurrentCulture, "Assert.NotEqual() Failure: {0} are equal", collectionDisplay ?? "Collections")
diff --git a/SetAsserts.cs b/SetAsserts.cs
index 663a59e8d95..f7bd5a3de1b 100644
--- a/SetAsserts.cs
+++ b/SetAsserts.cs
@@ -77,6 +77,18 @@ partial class Assert
HashSet set) =>
Contains(expected, (ISet)set);
+ ///
+ /// Verifies that the sorted hashset contains the given object.
+ ///
+ /// The type of the object to be verified
+ /// The object expected to be in the set
+ /// The set to be inspected
+ /// Thrown when the object is not present in the set
+ public static void Contains(
+ T expected,
+ SortedSet set) =>
+ Contains(expected, (ISet)set);
+
#if XUNIT_IMMUTABLE_COLLECTIONS
///
/// Verifies that the immutable hashset contains the given object.
@@ -89,6 +101,18 @@ partial class Assert
T expected,
ImmutableHashSet set) =>
Contains(expected, (ISet)set);
+
+ ///
+ /// Verifies that the immutable sorted hashset contains the given object.
+ ///
+ /// The type of the object to be verified
+ /// The object expected to be in the set
+ /// The set to be inspected
+ /// Thrown when the object is not present in the set
+ public static void Contains(
+ T expected,
+ ImmutableSortedSet set) =>
+ Contains(expected, (ISet)set);
#endif
///
@@ -145,6 +169,18 @@ partial class Assert
HashSet set) =>
DoesNotContain(expected, (ISet)set);
+ ///
+ /// Verifies that the sorted hashset does not contain the given item.
+ ///
+ /// The type of the object to be verified
+ /// The object expected to be in the set
+ /// The set to be inspected
+ /// Thrown when the object is not present in the set
+ public static void DoesNotContain(
+ T expected,
+ SortedSet set) =>
+ DoesNotContain(expected, (ISet)set);
+
#if XUNIT_IMMUTABLE_COLLECTIONS
///
/// Verifies that the immutable hashset does not contain the given item.
@@ -157,6 +193,18 @@ partial class Assert
T expected,
ImmutableHashSet set) =>
DoesNotContain(expected, (ISet)set);
+
+ ///
+ /// Verifies that the immutable sorted hashset does not contain the given item.
+ ///
+ /// The type of the object to be verified
+ /// The object expected to be in the set
+ /// The set to be inspected
+ /// Thrown when the object is not present in the set
+ public static void DoesNotContain(
+ T expected,
+ ImmutableSortedSet set) =>
+ DoesNotContain(expected, (ISet)set);
#endif
///
diff --git a/SpanAsserts.cs b/SpanAsserts.cs
index c299d186d9c..d9b73284fec 100644
--- a/SpanAsserts.cs
+++ b/SpanAsserts.cs
@@ -5,7 +5,6 @@
#endif
using System;
-using System.Globalization;
using Xunit.Sdk;
namespace Xunit
@@ -24,108 +23,6 @@ partial class Assert
// Also note that these classes will convert nulls into empty arrays automatically, since there
// is no way to represent a null readonly struct.
- ///
- /// Verifies that a span contains a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- Span expectedSubSpan,
- Span actualSpan) =>
- Contains((ReadOnlySpan)expectedSubSpan, (ReadOnlySpan)actualSpan, StringComparison.CurrentCulture);
-
- ///
- /// Verifies that a span contains a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- Span expectedSubSpan,
- ReadOnlySpan actualSpan) =>
- Contains((ReadOnlySpan)expectedSubSpan, actualSpan, StringComparison.CurrentCulture);
-
- ///
- /// Verifies that a span contains a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- ReadOnlySpan expectedSubSpan,
- Span actualSpan) =>
- Contains(expectedSubSpan, (ReadOnlySpan)actualSpan, StringComparison.CurrentCulture);
-
- ///
- /// Verifies that a span contains a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- ReadOnlySpan expectedSubSpan,
- ReadOnlySpan actualSpan) =>
- Contains(expectedSubSpan, actualSpan, StringComparison.CurrentCulture);
-
- ///
- /// Verifies that a span contains a given sub-span, using the given comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// The type of string comparison to perform
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- Span expectedSubSpan,
- Span actualSpan,
- StringComparison comparisonType = StringComparison.CurrentCulture) =>
- Contains((ReadOnlySpan)expectedSubSpan, (ReadOnlySpan)actualSpan, comparisonType);
-
- ///
- /// Verifies that a span contains a given sub-span, using the given comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// The type of string comparison to perform
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- Span expectedSubSpan,
- ReadOnlySpan actualSpan,
- StringComparison comparisonType = StringComparison.CurrentCulture) =>
- Contains((ReadOnlySpan)expectedSubSpan, actualSpan, comparisonType);
-
- ///
- /// Verifies that a span contains a given sub-span, using the given comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// The type of string comparison to perform
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- ReadOnlySpan expectedSubSpan,
- Span actualSpan,
- StringComparison comparisonType = StringComparison.CurrentCulture) =>
- Contains(expectedSubSpan, (ReadOnlySpan)actualSpan, comparisonType);
-
- ///
- /// Verifies that a span contains a given sub-span, using the given comparison type.
- ///
- /// The sub-span expected to be in the span
- /// The span to be inspected
- /// The type of string comparison to perform
- /// Thrown when the sub-span is not present inside the span
- public static void Contains(
- ReadOnlySpan expectedSubSpan,
- ReadOnlySpan actualSpan,
- StringComparison comparisonType = StringComparison.CurrentCulture)
- {
- if (actualSpan.IndexOf(expectedSubSpan, comparisonType) < 0)
- throw ContainsException.ForSubStringNotFound(
- expectedSubSpan.ToString(),
- actualSpan.ToString()
- );
- }
-
///
/// Verifies that a span contains a given sub-span
///
@@ -180,106 +77,6 @@ partial class Assert
);
}
- ///
- /// Verifies that a span does not contain a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected not to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is present inside the span
- public static void DoesNotContain(
- Span expectedSubSpan,
- Span actualSpan) =>
- DoesNotContain((ReadOnlySpan)expectedSubSpan, (ReadOnlySpan)actualSpan, StringComparison.CurrentCulture);
-
- ///
- /// Verifies that a span does not contain a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected not to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is present inside the span
- public static void DoesNotContain(
- Span expectedSubSpan,
- ReadOnlySpan actualSpan) =>
- DoesNotContain((ReadOnlySpan)expectedSubSpan, actualSpan, StringComparison.CurrentCulture);
-
- ///
- /// Verifies that a span does not contain a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected not to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is present inside the span
- public static void DoesNotContain(
- ReadOnlySpan expectedSubSpan,
- Span actualSpan) =>
- DoesNotContain(expectedSubSpan, (ReadOnlySpan)actualSpan, StringComparison.CurrentCulture);
-
- ///
- /// Verifies that a span does not contain a given sub-span, using the default comparison type.
- ///
- /// The sub-span expected not to be in the span
- /// The span to be inspected
- /// Thrown when the sub-span is present inside the span
- public static void DoesNotContain(
- ReadOnlySpan expectedSubSpan,
- ReadOnlySpan actualSpan) =>
- DoesNotContain((ReadOnlySpan)expectedSubSpan, (ReadOnlySpan