|
#pragma warning disable CA1031 // Do not catch general exception types
#pragma warning disable CA1052 // Static holder types should be static
#pragma warning disable CA1720 // Identifier contains type name
#pragma warning disable CA2007 // Consider calling ConfigureAwait on the awaited task
#pragma warning disable IDE0018 // Inline variable declaration
#pragma warning disable IDE0019 // Use pattern matching
#pragma warning disable IDE0040 // Add accessibility modifiers
#pragma warning disable IDE0058 // Expression value is never used
#pragma warning disable IDE0063 // Use simple 'using' statement
#pragma warning disable IDE0066 // Convert switch statement to expression
#pragma warning disable IDE0161 // Convert to file-scoped namespace
#pragma warning disable IDE0305 // Simplify collection initialization
#if XUNIT_NULLABLE
#nullable enable
#else
// In case this is source-imported with global nullable enabled but no XUNIT_NULLABLE
#pragma warning disable CS8603
#pragma warning disable CS8604
#pragma warning disable CS8625
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Xunit.Sdk;
namespace Xunit
{
#if XUNIT_VISIBILITY_INTERNAL
internal
#else
public
#endif
partial class Assert
{
/// <summary>
/// Verifies that all items in the collection pass when executed against
/// action.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="collection">The collection</param>
/// <param name="action">The action to test each item against</param>
/// <exception cref="AllException">Thrown when the collection contains at least one non-matching element</exception>
public static void All<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
Action<T> action)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(action), action);
All(collection, (item, index) => action(item));
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="collection">The collection</param>
/// <param name="action">The action to test each item against</param>
/// <exception cref="AllException">Thrown when the collection contains at least one non-matching element</exception>
public static void All<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
Action<T, int> action)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(action), action);
var errors = new List<Tuple<int, string, Exception>>();
var idx = 0;
foreach (var item in collection)
{
try
{
action(item, idx);
}
catch (Exception ex)
{
errors.Add(new Tuple<int, string, Exception>(idx, ArgumentFormatter.Format(item), ex));
}
++idx;
}
if (errors.Count > 0)
throw AllException.ForFailures(idx, errors);
}
/// <summary>
/// Verifies that all items in the collection pass when executed against
/// action.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="collection">The collection</param>
/// <param name="action">The action to test each item against</param>
/// <exception cref="AllException">Thrown when the collection contains at least one non-matching element</exception>
public static async Task AllAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
Func<T, Task> action)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(action), action);
await AllAsync(collection, async (item, index) => await action(item));
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="collection">The collection</param>
/// <param name="action">The action to test each item against</param>
/// <exception cref="AllException">Thrown when the collection contains at least one non-matching element</exception>
public static async Task AllAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
Func<T, int, Task> action)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(action), action);
var errors = new List<Tuple<int, string, Exception>>();
var idx = 0;
foreach (var item in collection)
{
try
{
await action(item, idx);
}
catch (Exception ex)
{
errors.Add(new Tuple<int, string, Exception>(idx, ArgumentFormatter.Format(item), ex));
}
++idx;
}
if (errors.Count > 0)
throw AllException.ForFailures(idx, errors.ToArray());
}
/// <summary>
/// Verifies that a collection contains exactly a given number of elements, which meet
/// the criteria provided by the element inspectors.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="collection">The collection to be inspected</param>
/// <param name="elementInspectors">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.</param>
public static void Collection<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
params Action<T>[] elementInspectors)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(elementInspectors), elementInspectors);
using (var tracker = collection.AsTracker())
{
var index = 0;
foreach (var item in tracker)
{
try
{
if (index < elementInspectors.Length)
elementInspectors[index](item);
}
catch (Exception ex)
{
int? pointerIndent;
var formattedCollection = tracker.FormatIndexedMismatch(index, out pointerIndent);
throw CollectionException.ForMismatchedItem(ex, index, pointerIndent, formattedCollection);
}
index++;
}
if (tracker.IterationCount != elementInspectors.Length)
throw CollectionException.ForMismatchedItemCount(elementInspectors.Length, tracker.IterationCount, tracker.FormatStart());
}
}
/// <summary>
/// Verifies that a collection contains exactly a given number of elements, which meet
/// the criteria provided by the element inspectors.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="collection">The collection to be inspected</param>
/// <param name="elementInspectors">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.</param>
public static async Task CollectionAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
params Func<T, Task>[] elementInspectors)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(elementInspectors), elementInspectors);
using (var tracker = collection.AsTracker())
{
var index = 0;
foreach (var item in tracker)
{
try
{
if (index < elementInspectors.Length)
await elementInspectors[index](item);
}
catch (Exception ex)
{
int? pointerIndent;
var formattedCollection = tracker.FormatIndexedMismatch(index, out pointerIndent);
throw CollectionException.ForMismatchedItem(ex, index, pointerIndent, formattedCollection);
}
index++;
}
if (tracker.IterationCount != elementInspectors.Length)
throw CollectionException.ForMismatchedItemCount(elementInspectors.Length, tracker.IterationCount, tracker.FormatStart());
}
}
/// <summary>
/// Verifies that a collection contains a given object.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="expected">The object expected to be in the collection</param>
/// <param name="collection">The collection to be inspected</param>
/// <exception cref="ContainsException">Thrown when the object is not present in the collection</exception>
public static void Contains<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
T expected,
IEnumerable<T> collection)
{
GuardArgumentNotNull(nameof(collection), collection);
// 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<T>;
if (set != null)
{
Contains(expected, set);
return;
}
#if NET5_0_OR_GREATER
var readOnlySet = collection as IReadOnlySet<T>;
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<T>());
}
/// <summary>
/// Verifies that a collection contains a given object, using an equality comparer.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="expected">The object expected to be in the collection</param>
/// <param name="collection">The collection to be inspected</param>
/// <param name="comparer">The comparer used to equate objects in the collection with the expected object</param>
/// <exception cref="ContainsException">Thrown when the object is not present in the collection</exception>
public static void Contains<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
T expected,
IEnumerable<T> collection,
IEqualityComparer<T> comparer)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(comparer), comparer);
using (var tracker = collection.AsTracker())
if (!tracker.Contains(expected, comparer))
throw ContainsException.ForCollectionItemNotFound(ArgumentFormatter.Format(expected), tracker.FormatStart());
}
/// <summary>
/// Verifies that a collection contains a given object.
/// </summary>
/// <typeparam name="T">The type of the object to be verified</typeparam>
/// <param name="collection">The collection to be inspected</param>
/// <param name="filter">The filter used to find the item you're ensuring the collection contains</param>
/// <exception cref="ContainsException">Thrown when the object is not present in the collection</exception>
public static void Contains<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
Predicate<T> filter)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(filter), filter);
using (var tracker = collection.AsTracker())
{
foreach (var item in tracker)
if (filter(item))
return;
throw ContainsException.ForCollectionFilterNotMatched(tracker.FormatStart());
}
}
/// <summary>
/// Verifies that a collection contains each object only once.
/// </summary>
/// <typeparam name="T">The type of the object to be compared</typeparam>
/// <param name="collection">The collection to be inspected</param>
/// <exception cref="DistinctException">Thrown when an object is present inside the collection more than once</exception>
public static void Distinct<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(IEnumerable<T> collection) =>
Distinct<T>(collection, EqualityComparer<T>.Default);
/// <summary>
/// Verifies that a collection contains each object only once.
/// </summary>
/// <typeparam name="T">The type of the object to be compared</typeparam>
/// <param name="collection">The collection to be inspected</param>
/// <param name="comparer">The comparer used to equate objects in the collection with the expected object</param>
/// <exception cref="DistinctException">Thrown when an object is present inside the collection more than once</exception>
public static void Distinct<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
IEqualityComparer<T> comparer)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(comparer), comparer);
using (var tracker = collection.AsTracker())
{
var set = new HashSet<T>(comparer);
foreach (var item in tracker)
if (!set.Add(item))
throw DistinctException.ForDuplicateItem(ArgumentFormatter.Format(item), tracker.FormatStart());
}
}
/// <summary>
/// Verifies that a collection does not contain a given object.
/// </summary>
/// <typeparam name="T">The type of the object to be compared</typeparam>
/// <param name="expected">The object that is expected not to be in the collection</param>
/// <param name="collection">The collection to be inspected</param>
/// <exception cref="DoesNotContainException">Thrown when the object is present inside the container</exception>
public static void DoesNotContain<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
T expected,
IEnumerable<T> collection)
{
GuardArgumentNotNull(nameof(collection), collection);
// 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<T>;
if (set != null)
{
DoesNotContain(expected, set);
return;
}
#if NET5_0_OR_GREATER
var readOnlySet = collection as IReadOnlySet<T>;
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<T>());
}
/// <summary>
/// Verifies that a collection does not contain a given object, using an equality comparer.
/// </summary>
/// <typeparam name="T">The type of the object to be compared</typeparam>
/// <param name="expected">The object that is expected not to be in the collection</param>
/// <param name="collection">The collection to be inspected</param>
/// <param name="comparer">The comparer used to equate objects in the collection with the expected object</param>
/// <exception cref="DoesNotContainException">Thrown when the object is present inside the collection</exception>
public static void DoesNotContain<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
T expected,
IEnumerable<T> collection,
IEqualityComparer<T> comparer)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(comparer), comparer);
using (var tracker = collection.AsTracker())
{
var index = 0;
foreach (var item in tracker)
{
if (comparer.Equals(item, expected))
{
int? pointerIndent;
var formattedCollection = tracker.FormatIndexedMismatch(index, out pointerIndent);
throw DoesNotContainException.ForCollectionItemFound(
ArgumentFormatter.Format(expected),
index,
pointerIndent,
formattedCollection
);
}
++index;
}
}
}
/// <summary>
/// Verifies that a collection does not contain a given object.
/// </summary>
/// <typeparam name="T">The type of the object to be compared</typeparam>
/// <param name="collection">The collection to be inspected</param>
/// <param name="filter">The filter used to find the item you're ensuring the collection does not contain</param>
/// <exception cref="DoesNotContainException">Thrown when the object is present inside the collection</exception>
public static void DoesNotContain<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
Predicate<T> filter)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(filter), filter);
using (var tracker = collection.AsTracker())
{
var index = 0;
foreach (var item in tracker)
{
if (filter(item))
{
int? pointerIndent;
var formattedCollection = tracker.FormatIndexedMismatch(index, out pointerIndent);
throw DoesNotContainException.ForCollectionFilterMatched(
index,
pointerIndent,
formattedCollection
);
}
++index;
}
}
}
/// <summary>
/// Verifies that a collection is empty.
/// </summary>
/// <param name="collection">The collection to be inspected</param>
/// <exception cref="ArgumentNullException">Thrown when the collection is null</exception>
/// <exception cref="EmptyException">Thrown when the collection is not empty</exception>
public static void Empty(IEnumerable collection)
{
GuardArgumentNotNull(nameof(collection), collection);
using (var tracker = collection.AsTracker())
{
var enumerator = tracker.GetEnumerator();
if (enumerator.MoveNext())
throw EmptyException.ForNonEmptyCollection(tracker.FormatStart());
}
}
/// <summary>
/// Verifies that two sequences are equivalent, using a default comparer.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <exception cref="EqualException">Thrown when the objects are not equal</exception>
public static void Equal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
#if XUNIT_NULLABLE
IEnumerable<T>? expected,
IEnumerable<T>? actual) =>
#else
IEnumerable<T> expected,
IEnumerable<T> actual) =>
#endif
Equal(expected, actual, GetEqualityComparer<IEnumerable<T>>());
/// <summary>
/// Verifies that two sequences are equivalent, using a custom equatable comparer.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <param name="comparer">The comparer used to compare the two objects</param>
/// <exception cref="EqualException">Thrown when the objects are not equal</exception>
public static void Equal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
#if XUNIT_NULLABLE
IEnumerable<T>? expected,
IEnumerable<T>? actual,
#else
IEnumerable<T> expected,
IEnumerable<T> actual,
#endif
IEqualityComparer<T> comparer) =>
Equal(expected, actual, GetEqualityComparer<IEnumerable<T>>(new AssertEqualityComparerAdapter<T>(comparer)));
/// <summary>
/// Verifies that two collections are equal, using a comparer function against
/// items in the two collections.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <param name="comparer">The function to compare two items for equality</param>
public static void Equal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces |
DynamicallyAccessedMemberTypes.PublicFields |
DynamicallyAccessedMemberTypes.NonPublicFields |
DynamicallyAccessedMemberTypes.PublicProperties |
DynamicallyAccessedMemberTypes.NonPublicProperties |
DynamicallyAccessedMemberTypes.PublicMethods)] T>(
#if XUNIT_NULLABLE
IEnumerable<T>? expected,
IEnumerable<T>? actual,
#else
IEnumerable<T> expected,
IEnumerable<T> actual,
#endif
Func<T, T, bool> comparer) =>
Equal(expected, actual, AssertEqualityComparer<T>.FromComparer(comparer));
/// <summary>
/// Verifies that a collection is not empty.
/// </summary>
/// <param name="collection">The collection to be inspected</param>
/// <exception cref="ArgumentNullException">Thrown when a null collection is passed</exception>
/// <exception cref="NotEmptyException">Thrown when the collection is empty</exception>
public static void NotEmpty(IEnumerable collection)
{
GuardArgumentNotNull(nameof(collection), collection);
var enumerator = collection.GetEnumerator();
try
{
if (!enumerator.MoveNext())
throw NotEmptyException.ForNonEmptyCollection();
}
finally
{
(enumerator as IDisposable)?.Dispose();
}
}
/// <summary>
/// Verifies that two sequences are not equivalent, using a default comparer.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected object</param>
/// <param name="actual">The actual object</param>
/// <exception cref="NotEqualException">Thrown when the objects are equal</exception>
public static void NotEqual<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
#if XUNIT_NULLABLE
IEnumerable<T>? expected,
IEnumerable<T>? actual) =>
#else
IEnumerable<T> expected,
IEnumerable<T> actual) =>
#endif
NotEqual(expected, actual, GetEqualityComparer<IEnumerable<T>>());
/// <summary>
/// Verifies that two sequences are not equivalent, using a custom equality comparer.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected object</param>
/// <param name="actual">The actual object</param>
/// <param name="comparer">The comparer used to compare the two objects</param>
/// <exception cref="NotEqualException">Thrown when the objects are equal</exception>
public static void NotEqual<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
#if XUNIT_NULLABLE
IEnumerable<T>? expected,
IEnumerable<T>? actual,
#else
IEnumerable<T> expected,
IEnumerable<T> actual,
#endif
IEqualityComparer<T> comparer) =>
NotEqual(expected, actual, GetEqualityComparer<IEnumerable<T>>(new AssertEqualityComparerAdapter<T>(comparer)));
/// <summary>
/// Verifies that two collections are not equal, using a comparer function against
/// items in the two collections.
/// </summary>
/// <typeparam name="T">The type of the objects to be compared</typeparam>
/// <param name="expected">The expected value</param>
/// <param name="actual">The value to be compared against</param>
/// <param name="comparer">The function to compare two items for equality</param>
public static void NotEqual<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces |
DynamicallyAccessedMemberTypes.PublicFields |
DynamicallyAccessedMemberTypes.NonPublicFields |
DynamicallyAccessedMemberTypes.PublicProperties |
DynamicallyAccessedMemberTypes.NonPublicProperties |
DynamicallyAccessedMemberTypes.PublicMethods)] T>(
#if XUNIT_NULLABLE
IEnumerable<T>? expected,
IEnumerable<T>? actual,
#else
IEnumerable<T> expected,
IEnumerable<T> actual,
#endif
Func<T, T, bool> comparer) =>
NotEqual(expected, actual, AssertEqualityComparer<T>.FromComparer(comparer));
/// <summary>
/// Verifies that the given collection contains only a single
/// element of the given type.
/// </summary>
/// <param name="collection">The collection.</param>
/// <returns>The single item in the collection.</returns>
/// <exception cref="SingleException">Thrown when the collection does not contain
/// exactly one element.</exception>
#if XUNIT_NULLABLE
public static object? Single(IEnumerable collection)
#else
public static object Single(IEnumerable collection)
#endif
{
GuardArgumentNotNull(nameof(collection), collection);
return Single(collection.Cast<object>());
}
/// <summary>
/// Verifies that the given collection contains only a single
/// element of the given value. The collection may or may not
/// contain other values.
/// </summary>
/// <param name="collection">The collection.</param>
/// <param name="expected">The value to find in the collection.</param>
/// <returns>The single item in the collection.</returns>
/// <exception cref="SingleException">Thrown when the collection does not contain
/// exactly one element.</exception>
public static void Single(
IEnumerable collection,
#if XUNIT_NULLABLE
object? expected)
#else
object expected)
#endif
{
GuardArgumentNotNull(nameof(collection), collection);
GetSingleResult(collection.Cast<object>(), item => object.Equals(item, expected), ArgumentFormatter.Format(expected));
}
/// <summary>
/// Verifies that the given collection contains only a single
/// element of the given type.
/// </summary>
/// <typeparam name="T">The collection type.</typeparam>
/// <param name="collection">The collection.</param>
/// <returns>The single item in the collection.</returns>
/// <exception cref="SingleException">Thrown when the collection does not contain
/// exactly one element.</exception>
public static T Single<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(IEnumerable<T> collection)
{
GuardArgumentNotNull(nameof(collection), collection);
return GetSingleResult(collection, null, null);
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="T">The collection type.</typeparam>
/// <param name="collection">The collection.</param>
/// <param name="predicate">The item matching predicate.</param>
/// <returns>The single item in the filtered collection.</returns>
/// <exception cref="SingleException">Thrown when the filtered collection does
/// not contain exactly one element.</exception>
public static T Single<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
Predicate<T> predicate)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(predicate), predicate);
return GetSingleResult(collection, predicate, "(predicate expression)");
}
static T GetSingleResult<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] T>(
IEnumerable<T> collection,
#if XUNIT_NULLABLE
Predicate<T>? predicate,
string? expected)
#else
Predicate<T> predicate,
string expected)
#endif
{
var count = 0;
var index = 0;
var matchIndices = new List<int>();
var result = default(T);
using (var tracker = collection.AsTracker())
{
foreach (var item in tracker)
{
if (predicate == null || predicate(item))
{
if (++count == 1)
result = item;
if (predicate != null)
matchIndices.Add(index);
}
++index;
}
switch (count)
{
case 0:
throw SingleException.Empty(expected, tracker.FormatStart());
case 1:
#if XUNIT_NULLABLE
return result!;
#else
return result;
#endif
default:
throw SingleException.MoreThanOne(count, expected, tracker.FormatStart(), matchIndices);
}
}
}
}
}
|